From 2f876e09a9ec152fba95095e8450afa7aca2fc48 Mon Sep 17 00:00:00 2001 From: James Henry Date: Thu, 13 Feb 2025 00:37:42 +0400 Subject: [PATCH] fix(core): tweaks to nx init (#30002) ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes # --- docs/generated/devkit/NxJsonConfiguration.md | 7 + docs/generated/devkit/Workspace.md | 11 + packages/nx/src/adapter/compat.ts | 1 + .../src/command-line/init/command-object.ts | 18 +- .../implementation/add-nx-to-turborepo.ts | 64 +++++ .../implementation/deduce-default-base.ts | 46 +++ .../init/implementation/utils.spec.ts | 269 ++++++++++++++++++ .../command-line/init/implementation/utils.ts | 154 +++++++--- packages/nx/src/command-line/init/init-v2.ts | 64 +++-- packages/nx/src/config/nx-json.ts | 1 + 10 files changed, 570 insertions(+), 65 deletions(-) create mode 100644 packages/nx/src/command-line/init/implementation/add-nx-to-turborepo.ts create mode 100644 packages/nx/src/command-line/init/implementation/deduce-default-base.ts create mode 100644 packages/nx/src/command-line/init/implementation/utils.spec.ts diff --git a/docs/generated/devkit/NxJsonConfiguration.md b/docs/generated/devkit/NxJsonConfiguration.md index 252f8364ecdba..5ede8571a5b5f 100644 --- a/docs/generated/devkit/NxJsonConfiguration.md +++ b/docs/generated/devkit/NxJsonConfiguration.md @@ -18,6 +18,7 @@ Nx.json configuration ### Properties +- [$schema](../../devkit/documents/NxJsonConfiguration#$schema): string - [affected](../../devkit/documents/NxJsonConfiguration#affected): NxAffectedConfig - [cacheDirectory](../../devkit/documents/NxJsonConfiguration#cachedirectory): string - [cli](../../devkit/documents/NxJsonConfiguration#cli): Object @@ -47,6 +48,12 @@ Nx.json configuration ## Properties +### $schema + +• `Optional` **$schema**: `string` + +--- + ### affected • `Optional` **affected**: [`NxAffectedConfig`](../../devkit/documents/NxAffectedConfig) diff --git a/docs/generated/devkit/Workspace.md b/docs/generated/devkit/Workspace.md index 2edf84326c9d5..b739214a341dd 100644 --- a/docs/generated/devkit/Workspace.md +++ b/docs/generated/devkit/Workspace.md @@ -16,6 +16,7 @@ use ProjectsConfigurations or NxJsonConfiguration ### Properties +- [$schema](../../devkit/documents/Workspace#$schema): string - [affected](../../devkit/documents/Workspace#affected): NxAffectedConfig - [cacheDirectory](../../devkit/documents/Workspace#cachedirectory): string - [cli](../../devkit/documents/Workspace#cli): Object @@ -47,6 +48,16 @@ use ProjectsConfigurations or NxJsonConfiguration ## Properties +### $schema + +• `Optional` **$schema**: `string` + +#### Inherited from + +[NxJsonConfiguration](../../devkit/documents/NxJsonConfiguration).[$schema](../../devkit/documents/NxJsonConfiguration#$schema) + +--- + ### affected • `Optional` **affected**: [`NxAffectedConfig`](../../devkit/documents/NxAffectedConfig) diff --git a/packages/nx/src/adapter/compat.ts b/packages/nx/src/adapter/compat.ts index d5daa3c2d70ee..9c0f341fc0663 100644 --- a/packages/nx/src/adapter/compat.ts +++ b/packages/nx/src/adapter/compat.ts @@ -54,6 +54,7 @@ export const allowedProjectExtensions = [ // There are some props in here (root) that angular already knows about, // but it doesn't hurt to have them in here as well to help static analysis. export const allowedWorkspaceExtensions = [ + '$schema', 'implicitDependencies', 'affected', 'defaultBase', diff --git a/packages/nx/src/command-line/init/command-object.ts b/packages/nx/src/command-line/init/command-object.ts index bc7e38995d72e..726718f8952c8 100644 --- a/packages/nx/src/command-line/init/command-object.ts +++ b/packages/nx/src/command-line/init/command-object.ts @@ -7,13 +7,19 @@ export const yargsInitCommand: CommandModule = { 'Adds Nx to any type of workspace. It installs nx, creates an nx.json configuration file and optionally sets up remote caching. For more info, check https://nx.dev/recipes/adopting-nx.', builder: (yargs) => withInitOptions(yargs), handler: async (args: any) => { - const useV2 = await isInitV2(); - if (useV2) { - await require('./init-v2').initHandler(args); - } else { - await require('./init-v1').initHandler(args); + try { + const useV2 = await isInitV2(); + if (useV2) { + await require('./init-v2').initHandler(args); + } else { + await require('./init-v1').initHandler(args); + } + process.exit(0); + } catch { + // Ensure the cursor is always restored just in case the user has bailed during interactive prompts + process.stdout.write('\x1b[?25h'); + process.exit(1); } - process.exit(0); }, }; diff --git a/packages/nx/src/command-line/init/implementation/add-nx-to-turborepo.ts b/packages/nx/src/command-line/init/implementation/add-nx-to-turborepo.ts new file mode 100644 index 0000000000000..51e4bc372f866 --- /dev/null +++ b/packages/nx/src/command-line/init/implementation/add-nx-to-turborepo.ts @@ -0,0 +1,64 @@ +import { writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { readJsonFile, writeJsonFile } from '../../../utils/fileutils'; +import { output } from '../../../utils/output'; +import { getPackageManagerCommand } from '../../../utils/package-manager'; +import { InitArgs } from '../init-v1'; +import { + addDepsToPackageJson, + createNxJsonFromTurboJson, + runInstall, + updateGitIgnore, +} from './utils'; + +type Options = Pick; + +export async function addNxToTurborepo(_options: Options) { + const repoRoot = process.cwd(); + + output.log({ + title: 'Initializing Nx based on your old Turborepo configuration', + }); + + output.log({ + title: '💡 Did you know?', + bodyLines: [ + '- Turborepo requires you to maintain all your common scripts like "build", "lint", "test" in all your packages, as well as their applicable cache inputs and outputs.', + `- Nx is extensible and has plugins for the tools you use to infer all of this for you purely based on that tool's configuration file within your packages.`, + '', + ' - E.g. the `@nx/vite` plugin will infer the "build" script based on the existence of a vite.config.js file.', + ' - Therefore with zero package level config, `nx build my-app` knows to run the `vite build` CLI directly, with all Nx cache inputs and outputs automatically inferred.', + '', + `NOTE: None of your existing package.json scripts will be modified as part of this initialization process, you can already use them as-is with Nx, but you can learn more about the benefits of Nx's inferred tasks at https://nx.dev/concepts/inferred-tasks`, + ], + }); + + let nxJson = createNxJsonFromTurboJson(readJsonFile('turbo.json')); + const nxJsonPath = join(repoRoot, 'nx.json'); + + // Turborepo workspaces usually have prettier installed, so try and match the formatting before writing the file + try { + const prettier = await import('prettier'); + const config = await prettier.resolveConfig(repoRoot); + writeFileSync( + nxJsonPath, + // @ts-ignore - Always await prettier.format, in modern versions it's async + await prettier.format(JSON.stringify(nxJson, null, 2), { + ...(config ?? {}), + parser: 'json', + }) + ); + } catch (err) { + // Apply fallback JSON write + writeJsonFile(nxJsonPath, nxJson); + } + + const pmc = getPackageManagerCommand(); + + updateGitIgnore(repoRoot); + addDepsToPackageJson(repoRoot); + + output.log({ title: '📦 Installing dependencies' }); + + runInstall(repoRoot, pmc); +} diff --git a/packages/nx/src/command-line/init/implementation/deduce-default-base.ts b/packages/nx/src/command-line/init/implementation/deduce-default-base.ts new file mode 100644 index 0000000000000..ed4c7c9f84bac --- /dev/null +++ b/packages/nx/src/command-line/init/implementation/deduce-default-base.ts @@ -0,0 +1,46 @@ +import { execSync } from 'node:child_process'; +import { deduceDefaultBase as gitInitDefaultBase } from '../../../utils/default-base'; + +export function deduceDefaultBase() { + try { + execSync(`git rev-parse --verify main`, { + stdio: ['ignore', 'ignore', 'ignore'], + windowsHide: false, + }); + return 'main'; + } catch { + try { + execSync(`git rev-parse --verify dev`, { + stdio: ['ignore', 'ignore', 'ignore'], + windowsHide: false, + }); + return 'dev'; + } catch { + try { + execSync(`git rev-parse --verify develop`, { + stdio: ['ignore', 'ignore', 'ignore'], + windowsHide: false, + }); + return 'develop'; + } catch { + try { + execSync(`git rev-parse --verify next`, { + stdio: ['ignore', 'ignore', 'ignore'], + windowsHide: false, + }); + return 'next'; + } catch { + try { + execSync(`git rev-parse --verify master`, { + stdio: ['ignore', 'ignore', 'ignore'], + windowsHide: false, + }); + return 'master'; + } catch { + return gitInitDefaultBase(); + } + } + } + } + } +} diff --git a/packages/nx/src/command-line/init/implementation/utils.spec.ts b/packages/nx/src/command-line/init/implementation/utils.spec.ts new file mode 100644 index 0000000000000..56713dedebce5 --- /dev/null +++ b/packages/nx/src/command-line/init/implementation/utils.spec.ts @@ -0,0 +1,269 @@ +jest.mock('./deduce-default-base', () => ({ + deduceDefaultBase: jest.fn(() => 'main'), +})); + +import { NxJsonConfiguration } from '../../../config/nx-json'; +import { createNxJsonFromTurboJson } from './utils'; + +describe('utils', () => { + describe('createNxJsonFromTurboJson', () => { + test.each<{ + description: string; + turbo: Record; + nx: NxJsonConfiguration; + }>([ + { + description: 'empty turbo.json', + turbo: {}, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + }, + }, + { + description: 'global dependencies', + turbo: { + globalDependencies: ['babel.config.json'], + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + namedInputs: { + sharedGlobals: ['{workspaceRoot}/babel.config.json'], + default: ['{projectRoot}/**/*', 'sharedGlobals'], + }, + }, + }, + { + description: 'global env variables', + turbo: { + globalEnv: ['NEXT_PUBLIC_API', 'NODE_ENV'], + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + namedInputs: { + sharedGlobals: [{ env: 'NEXT_PUBLIC_API' }, { env: 'NODE_ENV' }], + default: ['{projectRoot}/**/*', 'sharedGlobals'], + }, + }, + }, + { + description: 'basic task configuration with dependsOn', + turbo: { + tasks: { + build: { + dependsOn: ['^build'], + }, + }, + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + targetDefaults: { + build: { + dependsOn: ['^build'], + cache: true, + }, + }, + }, + }, + { + description: 'task configuration with outputs', + turbo: { + tasks: { + build: { + outputs: ['dist/**', '.next/**'], + }, + }, + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + targetDefaults: { + build: { + outputs: ['{projectRoot}/dist/**', '{projectRoot}/.next/**'], + cache: true, + }, + }, + }, + }, + { + description: 'task configuration with inputs', + turbo: { + tasks: { + build: { + inputs: ['src/**/*.tsx', 'test/**/*.tsx'], + }, + }, + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + targetDefaults: { + build: { + inputs: [ + '{projectRoot}/src/**/*.tsx', + '{projectRoot}/test/**/*.tsx', + ], + cache: true, + }, + }, + }, + }, + { + description: 'cache configuration', + turbo: { + tasks: { + build: { + cache: true, + }, + dev: { + cache: false, + }, + }, + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + targetDefaults: { + build: { + cache: true, + }, + dev: { + cache: false, + }, + }, + }, + }, + { + description: 'cache directory configuration', + turbo: { + cacheDir: './node_modules/.cache/turbo', + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + cacheDirectory: '.nx/cache', + }, + }, + { + description: 'skip project-specific task configurations', + turbo: { + tasks: { + build: { + dependsOn: ['^build'], + }, + 'docs#build': { + dependsOn: ['^build'], + outputs: ['www/**'], + }, + }, + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + targetDefaults: { + build: { + dependsOn: ['^build'], + cache: true, + }, + }, + }, + }, + { + description: 'complex configuration combining multiple features', + turbo: { + globalDependencies: ['babel.config.json'], + globalEnv: ['NODE_ENV'], + cacheDir: './node_modules/.cache/turbo', + tasks: { + build: { + dependsOn: ['^build'], + outputs: ['dist/**'], + inputs: ['src/**/*'], + cache: true, + }, + test: { + dependsOn: ['build'], + outputs: ['coverage/**'], + cache: true, + }, + dev: { + cache: false, + }, + }, + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + namedInputs: { + sharedGlobals: [ + '{workspaceRoot}/babel.config.json', + { env: 'NODE_ENV' }, + ], + default: ['{projectRoot}/**/*', 'sharedGlobals'], + }, + cacheDirectory: '.nx/cache', + targetDefaults: { + build: { + dependsOn: ['^build'], + outputs: ['{projectRoot}/dist/**'], + inputs: ['{projectRoot}/src/**/*'], + cache: true, + }, + test: { + dependsOn: ['build'], + outputs: ['{projectRoot}/coverage/**'], + cache: true, + }, + dev: { + cache: false, + }, + }, + }, + }, + { + description: 'turbo starter with $TURBO_DEFAULT$', + turbo: { + $schema: 'https://turbo.build/schema.json', + ui: 'tui', + tasks: { + build: { + dependsOn: ['^build'], + inputs: ['$TURBO_DEFAULT$', '.env*'], + outputs: ['.next/**', '!.next/cache/**'], + }, + lint: { + dependsOn: ['^lint'], + }, + 'check-types': { + dependsOn: ['^check-types'], + }, + dev: { + cache: false, + persistent: true, + }, + }, + }, + nx: { + $schema: './node_modules/nx/schemas/nx-schema.json', + targetDefaults: { + build: { + dependsOn: ['^build'], + inputs: ['{projectRoot}/**/*', '{projectRoot}/.env*'], + outputs: [ + '{projectRoot}/.next/**', + '!{projectRoot}/.next/cache/**', + ], + cache: true, + }, + lint: { + dependsOn: ['^lint'], + cache: true, + }, + 'check-types': { + dependsOn: ['^check-types'], + cache: true, + }, + dev: { + cache: false, + }, + }, + }, + }, + ])('$description', ({ turbo, nx }) => { + expect(createNxJsonFromTurboJson(turbo)).toEqual(nx); + }); + }); +}); diff --git a/packages/nx/src/command-line/init/implementation/utils.ts b/packages/nx/src/command-line/init/implementation/utils.ts index 50bcfabe42232..025c124e95460 100644 --- a/packages/nx/src/command-line/init/implementation/utils.ts +++ b/packages/nx/src/command-line/init/implementation/utils.ts @@ -19,7 +19,7 @@ import { existsSync, readFileSync, writeFileSync } from 'fs'; import { printSuccessMessage } from '../../../nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud'; import { repoUsesGithub } from '../../../nx-cloud/utilities/url-shorten'; import { connectWorkspaceToCloud } from '../../connect/connect-to-nx-cloud'; -import { deduceDefaultBase as gitInitDefaultBase } from '../../../utils/default-base'; +import { deduceDefaultBase } from './deduce-default-base'; export function createNxJsonFile( repoRoot: string, @@ -61,52 +61,127 @@ export function createNxJsonFile( delete nxJson.targetDefaults; } - nxJson.defaultBase ??= deduceDefaultBase(); + const defaultBase = deduceDefaultBase(); + // Do not add defaultBase if it is inferred to be the Nx default value of main + if (defaultBase !== 'main') { + nxJson.defaultBase ??= defaultBase; + } writeJsonFile(nxJsonPath, nxJson); } -function deduceDefaultBase() { - try { - execSync(`git rev-parse --verify main`, { - stdio: ['ignore', 'ignore', 'ignore'], - windowsHide: false, - }); - return 'main'; - } catch { - try { - execSync(`git rev-parse --verify dev`, { - stdio: ['ignore', 'ignore', 'ignore'], - windowsHide: false, - }); - return 'dev'; - } catch { - try { - execSync(`git rev-parse --verify develop`, { - stdio: ['ignore', 'ignore', 'ignore'], - windowsHide: false, - }); - return 'develop'; - } catch { - try { - execSync(`git rev-parse --verify next`, { - stdio: ['ignore', 'ignore', 'ignore'], - windowsHide: false, +export function createNxJsonFromTurboJson( + turboJson: Record +): NxJsonConfiguration { + const nxJson: NxJsonConfiguration = { + $schema: './node_modules/nx/schemas/nx-schema.json', + }; + + // Handle global dependencies + if (turboJson.globalDependencies?.length > 0) { + nxJson.namedInputs = { + sharedGlobals: turboJson.globalDependencies.map( + (dep) => `{workspaceRoot}/${dep}` + ), + default: ['{projectRoot}/**/*', 'sharedGlobals'], + }; + } + + // Handle global env vars + if (turboJson.globalEnv?.length > 0) { + nxJson.namedInputs = nxJson.namedInputs || {}; + nxJson.namedInputs.sharedGlobals = nxJson.namedInputs.sharedGlobals || []; + nxJson.namedInputs.sharedGlobals.push( + ...turboJson.globalEnv.map((env) => ({ env })) + ); + nxJson.namedInputs.default = nxJson.namedInputs.default || []; + if (!nxJson.namedInputs.default.includes('{projectRoot}/**/*')) { + nxJson.namedInputs.default.push('{projectRoot}/**/*'); + } + if (!nxJson.namedInputs.default.includes('sharedGlobals')) { + nxJson.namedInputs.default.push('sharedGlobals'); + } + } + + // Handle task configurations + if (turboJson.tasks) { + nxJson.targetDefaults = {}; + + for (const [taskName, taskConfig] of Object.entries(turboJson.tasks)) { + // Skip project-specific tasks (containing #) + if (taskName.includes('#')) continue; + + const config = taskConfig as any; + nxJson.targetDefaults[taskName] = {}; + + // Handle dependsOn + if (config.dependsOn?.length > 0) { + nxJson.targetDefaults[taskName].dependsOn = config.dependsOn; + } + + // Handle inputs + if (config.inputs?.length > 0) { + nxJson.targetDefaults[taskName].inputs = config.inputs + .map((input) => { + if (input === '$TURBO_DEFAULT$') { + return '{projectRoot}/**/*'; + } + // Don't add projectRoot if it's already there or if it's an env var + if ( + input.startsWith('{projectRoot}/') || + input.startsWith('{env.') || + input.startsWith('$') + ) + return input; + return `{projectRoot}/${input}`; + }) + .map((input) => { + // Don't add projectRoot if it's already there or if it's an env var + if ( + input.startsWith('{projectRoot}/') || + input.startsWith('{env.') || + input.startsWith('$') + ) + return input; + return `{projectRoot}/${input}`; }); - return 'next'; - } catch { - try { - execSync(`git rev-parse --verify master`, { - stdio: ['ignore', 'ignore', 'ignore'], - windowsHide: false, - }); - return 'master'; - } catch { - return gitInitDefaultBase(); + } + + // Handle outputs + if (config.outputs?.length > 0) { + nxJson.targetDefaults[taskName].outputs = config.outputs.map( + (output) => { + // Don't add projectRoot if it's already there + if (output.startsWith('{projectRoot}/')) return output; + // Handle negated patterns by adding projectRoot after the ! + if (output.startsWith('!')) { + return `!{projectRoot}/${output.slice(1)}`; + } + return `{projectRoot}/${output}`; } - } + ); } + + // Handle cache setting - true by default in Turbo + nxJson.targetDefaults[taskName].cache = config.cache !== false; } } + + /** + * The fact that cacheDir was in use suggests the user had a reason for deviating from the default. + * We can't know what that reason was, nor if it would still be applicable in Nx, but we can at least + * improve discoverability of the relevant Nx option by explicitly including it with its default value. + */ + if (turboJson.cacheDir) { + nxJson.cacheDirectory = '.nx/cache'; + } + + const defaultBase = deduceDefaultBase(); + // Do not add defaultBase if it is inferred to be the Nx default value of main + if (defaultBase !== 'main') { + nxJson.defaultBase ??= defaultBase; + } + + return nxJson; } export function addDepsToPackageJson( @@ -168,6 +243,7 @@ export async function initCloud( | 'nx-init-monorepo' | 'nx-init-nest' | 'nx-init-npm-repo' + | 'nx-init-turborepo' ) { const token = await connectWorkspaceToCloud({ installationSource, diff --git a/packages/nx/src/command-line/init/init-v2.ts b/packages/nx/src/command-line/init/init-v2.ts index f9f0eb088bb69..77fab508d4f63 100644 --- a/packages/nx/src/command-line/init/init-v2.ts +++ b/packages/nx/src/command-line/init/init-v2.ts @@ -1,13 +1,26 @@ import { existsSync } from 'fs'; -import { PackageJson } from '../../utils/package-json'; +import { prompt } from 'enquirer'; import { prerelease } from 'semver'; -import { output } from '../../utils/output'; -import { getPackageManagerCommand } from '../../utils/package-manager'; -import { generateDotNxSetup } from './implementation/dot-nx/add-nx-scripts'; +import { NxJsonConfiguration, readNxJson } from '../../config/nx-json'; import { runNxSync } from '../../utils/child-process'; import { readJsonFile } from '../../utils/fileutils'; +import { getPackageNameFromImportPath } from '../../utils/get-package-name-from-import-path'; +import { output } from '../../utils/output'; +import { PackageJson } from '../../utils/package-json'; +import { getPackageManagerCommand } from '../../utils/package-manager'; import { nxVersion } from '../../utils/versions'; +import { globWithWorkspaceContextSync } from '../../utils/workspace-context'; +import { connectExistingRepoToNxCloudPrompt } from '../connect/connect-to-nx-cloud'; +import { + configurePlugins, + runPackageManagerInstallPlugins, +} from './configure-plugins'; +import { addNxToMonorepo } from './implementation/add-nx-to-monorepo'; +import { addNxToNpmRepo } from './implementation/add-nx-to-npm-repo'; +import { addNxToTurborepo } from './implementation/add-nx-to-turborepo'; +import { addNxToAngularCliRepo } from './implementation/angular'; +import { generateDotNxSetup } from './implementation/dot-nx/add-nx-scripts'; import { createNxJsonFile, initCloud, @@ -15,18 +28,6 @@ import { printFinalMessage, updateGitIgnore, } from './implementation/utils'; -import { prompt } from 'enquirer'; -import { addNxToAngularCliRepo } from './implementation/angular'; -import { globWithWorkspaceContextSync } from '../../utils/workspace-context'; -import { connectExistingRepoToNxCloudPrompt } from '../connect/connect-to-nx-cloud'; -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; @@ -82,7 +83,32 @@ export async function initHandler(options: InitArgs): Promise { } const packageJson: PackageJson = readJsonFile('package.json'); - if (isMonorepo(packageJson)) { + const _isTurborepo = existsSync('turbo.json'); + const _isMonorepo = isMonorepo(packageJson); + + const learnMoreLink = _isTurborepo + ? 'https://nx.dev/recipes/adopting-nx/from-turborepo' + : _isMonorepo + ? 'https://nx.dev/getting-started/tutorials/npm-workspaces-tutorial' + : 'https://nx.dev/recipes/adopting-nx/adding-to-existing-project'; + + /** + * Turborepo users must have set up individual scripts already, and we keep the transition as minimal as possible. + * We log a message during the conversion process in addNxToTurborepo about how they can learn more about the power + * of Nx plugins and how it would allow them to infer all the relevant scripts automatically, including all cache + * inputs and outputs. + */ + if (_isTurborepo) { + await addNxToTurborepo({ + interactive: options.interactive, + }); + printFinalMessage({ + learnMoreLink, + }); + return; + } + + if (_isMonorepo) { await addNxToMonorepo({ interactive: options.interactive, nxCloud: false, @@ -93,9 +119,7 @@ export async function initHandler(options: InitArgs): Promise { nxCloud: false, }); } - const learnMoreLink = isMonorepo(packageJson) - ? 'https://nx.dev/getting-started/tutorials/npm-workspaces-tutorial' - : 'https://nx.dev/recipes/adopting-nx/adding-to-existing-project'; + const useNxCloud = options.nxCloud ?? (options.interactive ? await connectExistingRepoToNxCloudPrompt() : false); diff --git a/packages/nx/src/config/nx-json.ts b/packages/nx/src/config/nx-json.ts index d2e888863b360..d02f78eb53d1a 100644 --- a/packages/nx/src/config/nx-json.ts +++ b/packages/nx/src/config/nx-json.ts @@ -361,6 +361,7 @@ export interface NxSyncConfiguration { * @note: when adding properties here add them to `allowedWorkspaceExtensions` in adapter/compat.ts */ export interface NxJsonConfiguration { + $schema?: string; /** * Optional (additional) Nx.json configuration file which becomes a base for this one */