Project Init

This commit is contained in:
Muluhabt
2026-05-29 15:23:46 +03:00
commit 2fbc557aac
67387 changed files with 6063341 additions and 0 deletions

BIN
node_modules/nx/.DS_Store generated vendored Normal file

Binary file not shown.

22
node_modules/nx/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2017-2026 Narwhal Technologies Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

66
node_modules/nx/README.md generated vendored Normal file
View File

@@ -0,0 +1,66 @@
<p style="text-align: center;">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
<img alt="Nx - Smart Repos · Fast Builds" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
</picture>
</p>
<div style="text-align: center;">
[![CircleCI](https://circleci.com/gh/nrwl/nx.svg?style=svg)](https://circleci.com/gh/nrwl/nx)
[![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)]()
[![NPM Version](https://badge.fury.io/js/nx.svg)](https://www.npmjs.com/package/nx)
[![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)]()
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![Join the chat at https://gitter.im/nrwl-nx/community](https://badges.gitter.im/nrwl-nx/community.svg)](https://gitter.im/nrwl-nx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join us on the Official Nx Discord Server](https://img.shields.io/discord/1143497901675401286?label=discord)](https://go.nx.dev/community)
</div>
<hr>
# Nx: Smart Repos · Fast Builds
Get to green PRs in half the time. Nx optimizes your builds, scales your CI, and fixes failed PRs. Built for developers and AI agents.
## Getting Started
### Creating an Nx Workspace
**Using `npx`**
```bash
npx create-nx-workspace
```
**Using `npm init`**
```bash
npm init nx-workspace
```
**Using `yarn create`**
```bash
yarn create nx-workspace
```
### Adding Nx to an Existing Repository
Run:
```bash
npx nx@latest init
```
## Documentation & Resources
- [Nx.Dev: Documentation, Guides, Tutorials](https://nx.dev)
- [Intro to Nx](https://nx.dev/getting-started/intro)
- [Official Nx YouTube Channel](https://www.youtube.com/@NxDevtools)
- [Blog Posts About Nx](https://nx.dev/blog)
<p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
width="100%" alt="Nx - Smart Monorepos · Fast Builds"></a></p>

8
node_modules/nx/dist/bin/init-local.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { WorkspaceTypeAndRoot } from '../src/utils/find-workspace-root';
/**
* Nx is being run inside a workspace.
*
* @param workspace Relevant local workspace properties
*/
export declare function initLocal(workspace: WorkspaceTypeAndRoot): Promise<void>;
export declare function rewriteTargetsAndProjects(args: string[]): string[];

197
node_modules/nx/dist/bin/init-local.js generated vendored Normal file
View File

@@ -0,0 +1,197 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.initLocal = initLocal;
exports.rewriteTargetsAndProjects = rewriteTargetsAndProjects;
const perf_hooks_1 = require("perf_hooks");
const nx_commands_1 = require("../src/command-line/nx-commands");
const strip_indents_1 = require("../src/utils/strip-indents");
const client_1 = require("../src/daemon/client/client");
const enquirer_1 = require("enquirer");
const output_1 = require("../src/utils/output");
const analytics_1 = require("../src/analytics");
/**
* Nx is being run inside a workspace.
*
* @param workspace Relevant local workspace properties
*/
async function initLocal(workspace) {
process.env.NX_CLI_SET = 'true';
try {
// In case Nx Cloud forcibly exits while the TUI is running, ensure the terminal is restored etc.
process.on('exit', (...args) => {
if (typeof globalThis.tuiOnProcessExit === 'function') {
globalThis.tuiOnProcessExit(...args);
}
});
perf_hooks_1.performance.mark('init-local');
if (workspace.type !== 'nx' && shouldDelegateToAngularCLI()) {
console.warn((0, strip_indents_1.stripIndents) `Using Nx to run Angular CLI commands is deprecated and will be removed in a future version.
To run Angular CLI commands, use \`ng\`.`);
handleAngularCLIFallbacks(workspace);
return;
}
// Ensure NxConsole is installed if the user has it configured.
try {
await ensureNxConsoleInstalledViaDaemon();
}
catch { }
const command = process.argv[2];
if (command === 'run' || command === 'g' || command === 'generate') {
nx_commands_1.commandsObject.parse(process.argv.slice(2));
}
else if (isKnownCommand(command)) {
const newArgs = rewriteTargetsAndProjects(process.argv);
const help = newArgs.indexOf('--help');
const split = newArgs.indexOf('--');
if (help > -1 && (split === -1 || split > help)) {
nx_commands_1.commandsObject.showHelp();
process.exit(0);
}
else {
nx_commands_1.commandsObject.parse(newArgs);
}
}
else {
nx_commands_1.commandsObject.parse(process.argv.slice(2));
}
}
catch (e) {
console.error(e.message);
(0, analytics_1.flushAnalytics)();
process.exit(1);
}
}
function rewriteTargetsAndProjects(args) {
const newArgs = [args[2]];
let i = 3;
while (i < args.length) {
if (args[i] === '--') {
return [...newArgs, ...args.slice(i)];
}
else if (args[i] === '-p' ||
args[i] === '--projects' ||
args[i] === '--exclude' ||
args[i] === '--files' ||
args[i] === '-t' ||
args[i] === '--target' ||
args[i] === '--targets') {
newArgs.push(args[i]);
i++;
const items = [];
while (i < args.length && !args[i].startsWith('-')) {
items.push(args[i]);
i++;
}
newArgs.push(items.join(','));
}
else {
newArgs.push(args[i]);
++i;
}
}
return newArgs;
}
function isKnownCommand(command) {
const commands = [
...Object.keys(nx_commands_1.commandsObject
.getInternalMethods()
.getCommandInstance()
.getCommandHandlers()),
'g',
'dep-graph',
'affected:dep-graph',
'format',
'workspace-schematic',
'connect-to-nx-cloud',
'clear-cache',
'help',
];
return !command || command.startsWith('-') || commands.indexOf(command) > -1;
}
function shouldDelegateToAngularCLI() {
const command = process.argv[2];
const commands = [
'analytics',
'cache',
'completion',
'config',
'doc',
'update',
];
return commands.indexOf(command) > -1;
}
async function ensureNxConsoleInstalledViaDaemon() {
// Only proceed if daemon is available
if (!client_1.daemonClient.enabled() || !(await client_1.daemonClient.isServerAvailable())) {
return;
}
// Get status from daemon
const status = await client_1.daemonClient.getNxConsoleStatus();
// If we should prompt the user
if (status.shouldPrompt && process.stdout.isTTY) {
output_1.output.log({
title: "Install Nx's official editor extension to:",
bodyLines: [
'- Enable your AI assistant to do more by understanding your workspace',
'- Add IntelliSense for Nx configuration files',
'- Explore your workspace visually',
],
});
try {
const { shouldInstallNxConsole } = await (0, enquirer_1.prompt)({
type: 'confirm',
name: 'shouldInstallNxConsole',
message: 'Install Nx Console? (you can uninstall anytime)',
initial: true,
});
// Set preference and install if user said yes
const result = await client_1.daemonClient.setNxConsolePreferenceAndInstall(shouldInstallNxConsole);
if (result.installed) {
output_1.output.log({ title: 'Successfully installed Nx Console!' });
}
}
catch (error) {
// User cancelled or error occurred, save preference as false
await client_1.daemonClient.setNxConsolePreferenceAndInstall(false);
}
}
}
function handleAngularCLIFallbacks(workspace) {
if (process.argv[2] === 'update' && process.env.FORCE_NG_UPDATE != 'true') {
console.log(`Nx provides a much improved version of "ng update". It runs the same migrations, but allows you to:`);
console.log(`- rerun the same migration multiple times`);
console.log(`- reorder migrations, skip migrations`);
console.log(`- fix migrations that "almost work"`);
console.log(`- commit a partially migrated state`);
console.log(`- change versions of packages to match organizational requirements`);
console.log(`And, in general, it is lot more reliable for non-trivial workspaces. Read more at: https://nx.dev/getting-started/nx-and-angular#ng-update-and-nx-migrate`);
console.log(`Run "nx migrate latest" to update to the latest version of Nx.`);
console.log(`Running "ng update" can still be useful in some dev workflows, so we aren't planning to remove it.`);
console.log(`If you need to use it, run "FORCE_NG_UPDATE=true ng update".`);
}
else if (process.argv[2] === 'completion') {
if (!process.argv[3]) {
console.log(`"ng completion" is not natively supported by Nx.
Instead, you could try an Nx Editor Plugin for a visual tool to run Nx commands. If you're using VSCode, you can use the Nx Console plugin, or if you're using WebStorm, you could use one of the available community plugins.
For more information, see https://nx.dev/getting-started/editor-setup`);
}
}
else if (process.argv[2] === 'cache') {
console.log(`"ng cache" is not natively supported by Nx.
To clear the cache, you can delete the ".angular/cache" directory (or the directory configured by "cli.cache.path" in the "nx.json" file).
To update the cache configuration, you can directly update the relevant options in your "nx.json" file (https://angular.dev/reference/configs/workspace-config#cache-options).`);
}
else {
try {
// nx-ignore-next-line
const cli = require.resolve('@angular/cli/lib/init.js', {
paths: [workspace.dir],
});
require(cli);
}
catch (e) {
console.error(`Could not find '@angular/cli/lib/init.js' module in this workspace.`, e);
process.exit(1);
}
}
}

3
node_modules/nx/dist/bin/nx-cloud.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
import type { CloudTaskRunnerOptions } from '../src/nx-cloud/nx-cloud-tasks-runner-shell';
export declare function invokeCommandWithNxCloudClient(options: CloudTaskRunnerOptions): Promise<any>;

47
node_modules/nx/dist/bin/nx-cloud.js generated vendored Executable file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.invokeCommandWithNxCloudClient = invokeCommandWithNxCloudClient;
const get_cloud_options_1 = require("../src/nx-cloud/utilities/get-cloud-options");
const update_manager_1 = require("../src/nx-cloud/update-manager");
const output_1 = require("../src/utils/output");
const client_1 = require("../src/nx-cloud/utilities/client");
const command = process.argv[2];
const options = (0, get_cloud_options_1.getCloudOptions)();
Promise.resolve().then(async () => invokeCommandWithNxCloudClient(options));
async function invokeCommandWithNxCloudClient(options) {
try {
const client = await (0, client_1.getCloudClient)(options);
client.invoke(command);
}
catch (e) {
if (e instanceof client_1.UnknownCommandError) {
output_1.output.error({
title: `Unknown Command "${e.command}"`,
});
output_1.output.log({
title: 'Available Commands:',
bodyLines: e.availableCommands.map((c) => `- ${c}`),
});
process.exit(1);
}
const body = ['Cannot run commands from the `nx-cloud` CLI.'];
if (e instanceof update_manager_1.NxCloudEnterpriseOutdatedError) {
try {
// TODO: Remove this when all enterprise customers have updated.
// Try requiring the bin from the `nx-cloud` package.
return require('nx-cloud/bin/nx-cloud');
}
catch { }
body.push('If you are an Nx Enterprise customer, please reach out to your assigned Developer Productivity Engineer.', 'If you are NOT an Nx Enterprise customer but are seeing this message, please reach out to cloud-support@nrwl.io.');
}
if (e instanceof update_manager_1.NxCloudClientUnavailableError) {
body.unshift('You may be offline. Please try again when you are back online.');
}
output_1.output.error({
title: e.message,
bodyLines: body,
});
process.exit(1);
}
}

3
node_modules/nx/dist/bin/nx.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
import '../src/utils/enable-compile-cache';
import '../src/utils/perf-logging';

306
node_modules/nx/dist/bin/nx.js generated vendored Executable file
View File

@@ -0,0 +1,306 @@
#!/usr/bin/env node
"use strict";
// TODO: Remove this workaround once picocolors handles FORCE_COLOR=0 correctly
// See: https://github.com/alexeyraspopov/picocolors/issues/100
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
if (process.env.FORCE_COLOR === '0') {
process.env.NO_COLOR = '1';
delete process.env.FORCE_COLOR;
}
// Must be the first import — see enable-compile-cache.ts.
require("../src/utils/enable-compile-cache");
const find_workspace_root_1 = require("../src/utils/find-workspace-root");
const pc = tslib_1.__importStar(require("picocolors"));
const output_1 = require("../src/utils/output");
const installation_directory_1 = require("../src/utils/installation-directory");
const semver_1 = require("semver");
const strip_indents_1 = require("../src/utils/strip-indents");
const child_process_1 = require("child_process");
const module_1 = require("module");
const path_1 = require("path");
const fs_1 = require("fs");
const perf_hooks_1 = require("perf_hooks");
// Register the performance observer as early as possible so any
// `performance.mark` / `measure` anywhere downstream is captured. The module
// is side-effect only and its heavy deps (analytics, daemon logger) are
// lazy-loaded inside the observer callback, so the import itself is cheap.
require("../src/utils/perf-logging");
const isTsExt = (0, path_1.extname)(__filename).endsWith('.ts');
const pathToPkgJson = isTsExt ? '../package.json' : '../../package.json';
async function main() {
if (process.argv[2] !== 'report' &&
process.argv[2] !== '--version' &&
process.argv[2] !== '--help' &&
process.argv[2] !== 'reset') {
const { assertSupportedPlatform } = await import('../src/native/assert-supported-platform.js');
assertSupportedPlatform();
}
const workspace = (0, find_workspace_root_1.findWorkspaceRoot)(process.cwd());
// --version doesn't need any env / daemon / analytics state — skip dotenv
// loading (and the heavy modules it would pull in).
if (workspace && process.argv[2] !== '--version') {
const { workspaceDataDirectoryForWorkspace } = await import('../src/utils/cache-directory.js');
process.report.reportOnFatalError = true;
process.report.directory = workspaceDataDirectoryForWorkspace(workspace.dir);
const { loadRootEnvFiles } = await import('../src/utils/dotenv.js');
perf_hooks_1.performance.mark('loading dotenv files:start');
loadRootEnvFiles(workspace.dir);
perf_hooks_1.performance.mark('loading dotenv files:end');
perf_hooks_1.performance.measure('loading dotenv files', 'loading dotenv files:start', 'loading dotenv files:end');
}
// new is a special case because there is no local workspace to load
if (process.argv[2] === 'new' ||
process.argv[2] === '_migrate' ||
process.argv[2] === 'init' ||
process.argv[2] === 'configure-ai-agents' ||
process.argv[2] === 'mcp' ||
(process.argv[2] === 'graph' && !workspace)) {
process.env.NX_DAEMON = 'false';
(await import('nx/src/command-line/nx-commands')).commandsObject.argv;
}
else {
// polyfill rxjs observable to avoid issues with multiple version of Observable installed in node_modules
// https://twitter.com/BenLesh/status/1192478226385428483?s=20
if (!Symbol.observable)
Symbol.observable = Symbol('observable polyfill');
// Make sure that a local copy of Nx exists in workspace
let localNx;
try {
localNx = workspace && resolveNx(workspace);
}
catch {
localNx = null;
}
const isLocalInstall = localNx === resolveNx(null) || localNx === __filename;
const { LOCAL_NX_VERSION, GLOBAL_NX_VERSION } = determineNxVersions(localNx, workspace, isLocalInstall);
if (process.argv[2] === '--version') {
handleNxVersionCommand(LOCAL_NX_VERSION, GLOBAL_NX_VERSION);
}
if (!workspace && !isNxCloudCommand(process.argv[2])) {
handleNoWorkspace(GLOBAL_NX_VERSION);
}
if (!localNx && !isNxCloudCommand(process.argv[2])) {
handleMissingLocalInstallation(workspace ? workspace.dir : null);
}
// this file is already in the local workspace
if (isNxCloudCommand(process.argv[2])) {
const { daemonClient } = await import('../src/daemon/client/client.js');
if (!daemonClient.enabled() && workspace !== null) {
const { setupWorkspaceContext } = await import('../src/utils/workspace-context.js');
setupWorkspaceContext(workspace.dir);
}
await initAnalytics();
// nx-cloud commands can run without local Nx installation
process.env.NX_DAEMON = 'false';
(await import('nx/src/command-line/nx-commands')).commandsObject.argv;
}
else if (isLocalInstall) {
const { daemonClient } = await import('../src/daemon/client/client.js');
if (!daemonClient.enabled() && workspace !== null) {
const { setupWorkspaceContext } = await import('../src/utils/workspace-context.js');
setupWorkspaceContext(workspace.dir);
}
await initAnalytics();
const { initLocal } = await import('./init-local.js');
await initLocal(workspace);
}
else if (localNx) {
// Nx is being run from globally installed CLI - hand off to the local
// Don't start analytics, connect to the DB, or set up the workspace
// context here — the local Nx will handle it when it runs its own bin/nx.ts
warnIfUsingOutdatedGlobalInstall(GLOBAL_NX_VERSION, LOCAL_NX_VERSION);
if (localNx.includes('.nx')) {
const nxWrapperPath = localNx.replace(/\.nx.*/, '.nx/') + 'nxw.js';
require(nxWrapperPath);
}
else {
require(localNx);
}
}
}
}
function handleNoWorkspace(globalNxVersion) {
output_1.output.log({
title: `The current directory isn't part of an Nx workspace.`,
bodyLines: [
`To create a workspace run:`,
pc.bold(pc.white(`npx create-nx-workspace@latest <workspace name>`)),
'',
`To add Nx to an existing workspace with a workspace-specific nx.json, run:`,
pc.bold(pc.white(`npx nx@latest init`)),
],
});
output_1.output.note({
title: `For more information please visit https://nx.dev/`,
});
warnIfUsingOutdatedGlobalInstall(globalNxVersion);
process.exit(1);
}
function handleNxVersionCommand(LOCAL_NX_VERSION, GLOBAL_NX_VERSION) {
console.log((0, strip_indents_1.stripIndents) `Nx Version:
- Local: ${LOCAL_NX_VERSION ? 'v' + LOCAL_NX_VERSION : 'Not found'}
- Global: ${GLOBAL_NX_VERSION ? 'v' + GLOBAL_NX_VERSION : 'Not found'}`);
process.exit(0);
}
function determineNxVersions(localNx, workspace, isLocalInstall) {
const LOCAL_NX_VERSION = localNx
? getLocalNxVersion(workspace)
: null;
const GLOBAL_NX_VERSION = isLocalInstall
? null
: require(pathToPkgJson).version;
globalThis.GLOBAL_NX_VERSION ??= GLOBAL_NX_VERSION;
return { LOCAL_NX_VERSION, GLOBAL_NX_VERSION };
}
function resolveNx(workspace) {
// root relative to location of the nx bin
const globalsRoot = (0, path_1.join)(__dirname, '../../../../');
const root = workspace ? workspace.dir : globalsRoot;
// Use createRequire to resolve from outside the nx package,
// avoiding self-referencing caused by the exports field
// prefer Nx installed in .nx/installation
try {
const installPath = (0, installation_directory_1.getNxInstallationPath)(root);
if ((0, fs_1.existsSync)(installPath)) {
const installRequire = (0, module_1.createRequire)((0, path_1.join)(installPath, 'package.json'));
return installRequire.resolve('nx/bin/nx.js');
}
}
catch { }
// check for root install
const rootRequire = (0, module_1.createRequire)((0, path_1.join)(root, 'package.json'));
return rootRequire.resolve('nx/bin/nx.js');
}
function isNxCloudCommand(command) {
const nxCloudCommands = [
'start-ci-run',
'start-agent',
'stop-all-agents',
'complete-ci-run',
'login',
'logout',
'connect',
'view-logs',
'fix-ci',
'record',
'download-cloud-client',
];
return nxCloudCommands.includes(command);
}
let analyticsStarted = false;
async function initAnalytics() {
const { ensureAnalyticsPreferenceSet } = await import('../src/utils/analytics-prompt.js');
const { startAnalytics } = await import('../src/analytics/index.js');
try {
await ensureAnalyticsPreferenceSet();
}
catch { }
await startAnalytics();
analyticsStarted = true;
}
function handleMissingLocalInstallation(detectedWorkspaceRoot) {
output_1.output.error({
title: detectedWorkspaceRoot
? `Could not find Nx modules at "${detectedWorkspaceRoot}".`
: `Could not find Nx modules in this workspace.`,
bodyLines: [`Have you run ${pc.bold(pc.white(`npm/yarn install`))}?`],
});
process.exit(1);
}
/**
* Assumes currently running Nx is global install.
* Warns if out of date by 1 major version or more.
*/
function warnIfUsingOutdatedGlobalInstall(globalNxVersion, localNxVersion) {
// Never display this warning if Nx is already running via Nx
if (process.env.NX_CLI_SET) {
return;
}
const isOutdatedGlobalInstall = checkOutdatedGlobalInstallation(globalNxVersion, localNxVersion);
// Using a global Nx Install
if (isOutdatedGlobalInstall) {
const bodyLines = localNxVersion
? [
`Your repository uses a higher version of Nx (${localNxVersion}) than your global CLI version (${globalNxVersion})`,
]
: [];
bodyLines.push('For more information, see https://nx.dev/more-concepts/global-nx');
output_1.output.warn({
title: `It's time to update Nx 🎉`,
bodyLines,
});
}
}
function checkOutdatedGlobalInstallation(globalNxVersion, localNxVersion) {
// We aren't running a global install, so we can't know if its outdated.
if (!globalNxVersion) {
return false;
}
if (localNxVersion) {
// If the global Nx install is at least a major version behind the local install, warn.
return (0, semver_1.major)(globalNxVersion) < (0, semver_1.major)(localNxVersion);
}
// No local installation was detected. This can happen if the user is running a global install
// that contains an older version of Nx, which is unable to detect the local installation. The most
// recent case where this would have happened would be when we stopped generating workspace.json by default,
// as older global installations used it to determine the workspace root. This only be hit in rare cases,
// but can provide valuable insights for troubleshooting.
const latestVersionOfNx = getLatestVersionOfNx();
if (latestVersionOfNx && (0, semver_1.major)(globalNxVersion) < (0, semver_1.major)(latestVersionOfNx)) {
return true;
}
}
function getLocalNxVersion(workspace) {
try {
const searchPaths = (0, installation_directory_1.getNxRequirePaths)(workspace.dir);
for (const searchPath of searchPaths) {
if (!(0, fs_1.existsSync)(searchPath)) {
continue;
}
try {
const externalRequire = (0, module_1.createRequire)((0, path_1.join)(searchPath, 'package.json'));
const pkgJsonPath = externalRequire.resolve('nx/package.json');
return require(pkgJsonPath).version;
}
catch { }
}
}
catch { }
return null;
}
function _getLatestVersionOfNx() {
try {
return (0, child_process_1.execSync)('npm view nx@latest version', {
windowsHide: true,
})
.toString()
.trim();
}
catch {
try {
return (0, child_process_1.execSync)('pnpm view nx@latest version', {
windowsHide: true,
})
.toString()
.trim();
}
catch {
return null;
}
}
}
const getLatestVersionOfNx = ((fn) => {
let cache = null;
return () => cache || (cache = fn());
})(_getLatestVersionOfNx);
main().catch(async (error) => {
console.error(error);
if (analyticsStarted) {
// analyticsStarted implies '../src/analytics' is already in the module
// cache, so this resolves from cache without any disk work.
const { flushAnalytics } = await import('../src/analytics/index.js');
flushAnalytics();
}
process.exit(1);
});

1
node_modules/nx/dist/bin/post-install.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export {};

52
node_modules/nx/dist/bin/post-install.js generated vendored Normal file
View File

@@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const workspace_root_1 = require("../src/utils/workspace-root");
const fileutils_1 = require("../src/utils/fileutils");
const path_1 = require("path");
const assert_supported_platform_1 = require("../src/native/assert-supported-platform");
const update_manager_1 = require("../src/nx-cloud/update-manager");
const get_cloud_options_1 = require("../src/nx-cloud/utilities/get-cloud-options");
const nx_cloud_utils_1 = require("../src/utils/nx-cloud-utils");
const nx_json_1 = require("../src/config/nx-json");
const logger_1 = require("../src/utils/logger");
// The post install is not critical, to avoid any chance that it may hang
// we will kill this process after 30 seconds.
const postinstallTimeout = setTimeout(() => {
logger_1.logger.verbose('Nx post-install timed out.');
process.exit(0);
}, 30_000);
(async () => {
const start = new Date();
try {
if (isMainNxPackage() && (0, fileutils_1.fileExists)((0, path_1.join)(workspace_root_1.workspaceRoot, 'nx.json'))) {
(0, assert_supported_platform_1.assertSupportedPlatform)();
if ((0, nx_cloud_utils_1.isNxCloudUsed)((0, nx_json_1.readNxJson)())) {
await (0, update_manager_1.verifyOrUpdateNxCloudClient)((0, get_cloud_options_1.getCloudOptions)());
}
}
}
catch (e) {
logger_1.logger.verbose(e);
}
finally {
const end = new Date();
logger_1.logger.verbose(`Nx postinstall steps took ${end.getTime() - start.getTime()}ms`);
clearTimeout(postinstallTimeout);
process.exit(0);
}
})();
function isMainNxPackage() {
const mainNxPath = require.resolve('nx', {
paths: [workspace_root_1.workspaceRoot],
});
const thisNxPath = require.resolve('nx');
return mainNxPath === thisNxPath;
}
process.on('uncaughtException', (e) => {
logger_1.logger.verbose(e);
process.exit(0);
});
process.on('unhandledRejection', (e) => {
logger_1.logger.verbose(e);
process.exit(0);
});

1
node_modules/nx/dist/bin/run-executor.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export {};

66
node_modules/nx/dist/bin/run-executor.js generated vendored Normal file
View File

@@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const run_1 = require("../src/command-line/run/run");
if (process.env.NX_TERMINAL_OUTPUT_PATH) {
setUpOutputWatching(process.env.NX_TERMINAL_CAPTURE_STDERR === 'true', process.env.NX_STREAM_OUTPUT === 'true');
}
if (!process.env.NX_WORKSPACE_ROOT) {
console.error('Invalid Nx command invocation');
process.exit(1);
}
process.env.NX_CLI_SET = 'true';
/**
* We need to collect all stdout and stderr and store it, so the caching mechanism
* could store it.
*
* Writing stdout and stderr into different streams is too risky when using TTY.
*
* So we are simply monkey-patching the Javascript object. In this case the actual output will always be correct.
* And the cached output should be correct unless the CLI bypasses process.stdout or console.log and uses some
* C-binary to write to stdout.
*/
function setUpOutputWatching(captureStderr, streamOutput) {
const stdoutWrite = process.stdout._write;
const stderrWrite = process.stderr._write;
// The terminal output file gets out and err
const outputPath = process.env.NX_TERMINAL_OUTPUT_PATH;
const stdoutAndStderrLogFileHandle = (0, fs_1.openSync)(outputPath, 'w');
const onlyStdout = [];
process.stdout._write = (chunk, encoding, callback) => {
onlyStdout.push(chunk);
(0, fs_1.appendFileSync)(stdoutAndStderrLogFileHandle, chunk);
if (streamOutput) {
stdoutWrite.apply(process.stdout, [chunk, encoding, callback]);
}
else {
callback();
}
};
process.stderr._write = (chunk, encoding, callback) => {
(0, fs_1.appendFileSync)(stdoutAndStderrLogFileHandle, chunk);
if (streamOutput) {
stderrWrite.apply(process.stderr, [chunk, encoding, callback]);
}
else {
callback();
}
};
process.on('exit', (code) => {
// when the process exits successfully, and we are not asked to capture stderr
// override the file with only stdout
if (code === 0 && !captureStderr) {
(0, fs_1.writeFileSync)(outputPath, onlyStdout.join(''));
}
});
}
process.on('message', async (message) => {
try {
const statusCode = await (0, run_1.run)(process.cwd(), process.env.NX_WORKSPACE_ROOT, message.targetDescription, message.overrides, message.isVerbose, message.taskGraph);
process.exit(statusCode);
}
catch (e) {
console.error(e);
process.exit(1);
}
});

4
node_modules/nx/dist/plugins/package-json.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { ProjectConfiguration } from '../src/config/workspace-json-project-json';
import { PluginCache } from '../src/utils/plugin-cache-utils';
export type PackageJsonConfigurationCache = PluginCache<ProjectConfiguration>;
export declare function readPackageJsonConfigurationCache(): PackageJsonConfigurationCache;

38
node_modules/nx/dist/plugins/package-json.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readPackageJsonConfigurationCache = readPackageJsonConfigurationCache;
const plugins_1 = require("../src/project-graph/plugins");
const workspace_root_1 = require("../src/utils/workspace-root");
const package_json_1 = require("../src/plugins/package-json");
const cache_directory_1 = require("../src/utils/cache-directory");
const path_1 = require("path");
const fileutils_1 = require("../src/utils/fileutils");
const plugin_cache_utils_1 = require("../src/utils/plugin-cache-utils");
const cachePath = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'package-json.hash');
let packageJsonPluginCache = null;
function readPackageJsonConfigurationCache() {
packageJsonPluginCache = new plugin_cache_utils_1.PluginCache(cachePath);
return packageJsonPluginCache;
}
function writeCache() {
if (packageJsonPluginCache) {
packageJsonPluginCache.writeToDisk(cachePath);
}
}
const plugin = {
name: 'nx-all-package-jsons-plugin',
createNodesV2: [
'*/**/package.json',
(configFiles, options, context) => {
const cache = readPackageJsonConfigurationCache();
const patterns = (0, package_json_1.buildPackageJsonPatterns)(context.workspaceRoot, (f) => (0, fileutils_1.readJsonFile)((0, path_1.join)(context.workspaceRoot, f)));
const isInPackageJsonWorkspaces = (0, package_json_1.buildPackageJsonWorkspacesMatcher)(patterns);
const result = (0, plugins_1.createNodesFromFiles)((packageJsonPath) => (0, package_json_1.createNodeFromPackageJson)(packageJsonPath, workspace_root_1.workspaceRoot, cache, isInPackageJsonWorkspaces(packageJsonPath)), configFiles, options, context);
writeCache();
return result;
},
],
};
module.exports = plugin;
module.exports.readPackageJsonConfigurationCache =
readPackageJsonConfigurationCache;

1
node_modules/nx/dist/presets/core.json generated vendored Normal file
View File

@@ -0,0 +1 @@
{}

7
node_modules/nx/dist/presets/npm.json generated vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"pluginsConfig": {
"@nx/js": {
"analyzeSourceFiles": false
}
}
}

View File

@@ -0,0 +1,109 @@
import type { ChangelogChange } from '../../src/command-line/release/changelog';
import type { NxReleaseConfig } from '../../src/command-line/release/config/config';
import type { RemoteReleaseClient } from '../../src/command-line/release/utils/remote-release-clients/remote-release-client';
/**
* Re-export for ease of use in custom changelog renderers.
*/
export type { ChangelogChange };
/**
* The ChangelogRenderOptions are specific to each ChangelogRenderer implementation, and are taken
* from the user's nx.json configuration and passed as is into the ChangelogRenderer function.
*/
export type ChangelogRenderOptions = Record<string, unknown>;
/**
* When versioning projects independently and enabling `"updateDependents": "auto"`, there could
* be additional dependency bump information that is not captured in the commit data, but that nevertheless
* should be included in the rendered changelog.
*/
export type DependencyBump = {
dependencyName: string;
newVersion: string;
};
/**
* The specific options available to the default implementation of the ChangelogRenderer that nx exports
* for the common case.
*/
export interface DefaultChangelogRenderOptions extends ChangelogRenderOptions {
/**
* Whether or not the commit authors should be added to the bottom of the changelog in a "Thank You"
* section. Defaults to true.
*/
authors?: boolean;
/**
* If authors is enabled, controls whether or not to try to map the authors to their GitHub usernames
* using https://ungh.cc (from https://github.com/unjs/ungh) and, if needed, the GitHub search API via
* the gh CLI and the email addresses found in the commits.
* Defaults to true.
*/
applyUsernameToAuthors?: boolean;
/**
* Whether or not the commit references (such as commit and/or PR links) should be included in the changelog.
* Defaults to true.
*/
commitReferences?: boolean;
/**
* Whether or not to include the date in the version title. It can be set to false to disable it, or true to enable
* with the default of (YYYY-MM-DD). Defaults to true.
*/
versionTitleDate?: boolean;
}
export default class DefaultChangelogRenderer {
protected changes: ChangelogChange[];
protected changelogEntryVersion: string;
protected project: string | null;
protected entryWhenNoChanges: string | false;
protected changelogRenderOptions: DefaultChangelogRenderOptions;
protected isVersionPlans: boolean;
protected dependencyBumps?: DependencyBump[];
protected conventionalCommitsConfig: NxReleaseConfig['conventionalCommits'];
protected relevantChanges: ChangelogChange[];
protected breakingChanges: string[];
protected additionalChangesForAuthorsSection: ChangelogChange[];
protected remoteReleaseClient: RemoteReleaseClient<unknown>;
/**
* A ChangelogRenderer class takes in the determined changes and other relevant metadata
* and returns a string, or a Promise of a string of changelog contents (usually markdown).
*
* @param {Object} config The configuration object for the ChangelogRenderer
* @param {ChangelogChange[]} config.changes The collection of changes to show in the changelog
* @param {string} config.changelogEntryVersion The version for which we are rendering the current changelog entry
* @param {string | null} config.project The name of specific project to generate a changelog entry for, or `null` if the overall workspace changelog
* @param {string | false} config.entryWhenNoChanges The (already interpolated) string to use as the changelog entry when there are no changes, or `false` if no entry should be generated
* @param {boolean} config.isVersionPlans Whether or not Nx release version plans are the source of truth for the changelog entry
* @param {ChangelogRenderOptions} config.changelogRenderOptions The options specific to the ChangelogRenderer implementation
* @param {DependencyBump[]} config.dependencyBumps Optional list of additional dependency bumps that occurred as part of the release, outside of the change data
* @param {NxReleaseConfig['conventionalCommits']} config.conventionalCommitsConfig The configuration for conventional commits
* @param {RemoteReleaseClient} config.remoteReleaseClient The remote release client to use for formatting references
*/
constructor(config: {
changes: ChangelogChange[];
changelogEntryVersion: string;
project: string | null;
entryWhenNoChanges: string | false;
isVersionPlans: boolean;
changelogRenderOptions: DefaultChangelogRenderOptions;
dependencyBumps?: DependencyBump[];
conventionalCommitsConfig: NxReleaseConfig['conventionalCommits'];
remoteReleaseClient: RemoteReleaseClient<unknown>;
});
protected filterChanges(changes: ChangelogChange[], project: string | null): ChangelogChange[];
render(): Promise<string>;
protected preprocessChanges(): void;
protected shouldRenderEmptyEntry(): boolean;
protected renderEmptyEntry(): string;
protected renderVersionTitle(): string;
protected renderChangesByType(): string[];
protected hasBreakingChanges(): boolean;
protected renderBreakingChanges(): string[];
protected hasDependencyBumps(): boolean;
protected renderDependencyBumps(): string[];
protected shouldRenderAuthors(): boolean;
protected renderAuthors(): Promise<string[]>;
protected formatChange(change: ChangelogChange): string;
protected formatBreakingChangeBase(change: ChangelogChange): string;
protected formatBreakingChange(change: ChangelogChange): string;
protected groupChangesByType(): Record<string, ChangelogChange[]>;
protected groupChangesByScope(changes: ChangelogChange[]): Record<string, ChangelogChange[]>;
protected extractBreakingChangeExplanation(message: string): string | null;
protected formatName(name?: string): string;
}

View File

@@ -0,0 +1,367 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const semver_1 = require("semver");
class DefaultChangelogRenderer {
/**
* A ChangelogRenderer class takes in the determined changes and other relevant metadata
* and returns a string, or a Promise of a string of changelog contents (usually markdown).
*
* @param {Object} config The configuration object for the ChangelogRenderer
* @param {ChangelogChange[]} config.changes The collection of changes to show in the changelog
* @param {string} config.changelogEntryVersion The version for which we are rendering the current changelog entry
* @param {string | null} config.project The name of specific project to generate a changelog entry for, or `null` if the overall workspace changelog
* @param {string | false} config.entryWhenNoChanges The (already interpolated) string to use as the changelog entry when there are no changes, or `false` if no entry should be generated
* @param {boolean} config.isVersionPlans Whether or not Nx release version plans are the source of truth for the changelog entry
* @param {ChangelogRenderOptions} config.changelogRenderOptions The options specific to the ChangelogRenderer implementation
* @param {DependencyBump[]} config.dependencyBumps Optional list of additional dependency bumps that occurred as part of the release, outside of the change data
* @param {NxReleaseConfig['conventionalCommits']} config.conventionalCommitsConfig The configuration for conventional commits
* @param {RemoteReleaseClient} config.remoteReleaseClient The remote release client to use for formatting references
*/
constructor(config) {
this.changes = this.filterChanges(config.changes, config.project);
this.changelogEntryVersion = config.changelogEntryVersion;
this.project = config.project;
this.entryWhenNoChanges = config.entryWhenNoChanges;
this.isVersionPlans = config.isVersionPlans;
this.changelogRenderOptions = config.changelogRenderOptions;
this.dependencyBumps = config.dependencyBumps;
this.conventionalCommitsConfig = config.conventionalCommitsConfig;
this.remoteReleaseClient = config.remoteReleaseClient;
this.relevantChanges = [];
this.breakingChanges = [];
this.additionalChangesForAuthorsSection = [];
}
filterChanges(changes, project) {
if (project === null) {
return changes;
}
return changes.filter((c) => c.affectedProjects &&
(c.affectedProjects === '*' || c.affectedProjects.includes(project)));
}
async render() {
const sections = [];
this.preprocessChanges();
if (this.shouldRenderEmptyEntry()) {
return this.renderEmptyEntry();
}
sections.push([this.renderVersionTitle()]);
const changesByType = this.renderChangesByType();
if (changesByType.length > 0) {
sections.push(changesByType);
}
if (this.hasBreakingChanges()) {
sections.push(this.renderBreakingChanges());
}
if (this.hasDependencyBumps()) {
sections.push(this.renderDependencyBumps());
}
if (this.shouldRenderAuthors()) {
sections.push(await this.renderAuthors());
}
// Join sections with double newlines, and trim any extra whitespace
return sections
.filter((section) => section.length > 0)
.map((section) => section.join('\n').trim())
.join('\n\n')
.trim();
}
preprocessChanges() {
this.relevantChanges = [...this.changes];
this.breakingChanges = [];
this.additionalChangesForAuthorsSection = [];
// Filter out reverted changes
for (let i = this.relevantChanges.length - 1; i >= 0; i--) {
const change = this.relevantChanges[i];
if (change.type === 'revert' && change.revertedHashes) {
for (const revertedHash of change.revertedHashes) {
const revertedCommitIndex = this.relevantChanges.findIndex((c) => c.shortHash && revertedHash.startsWith(c.shortHash));
if (revertedCommitIndex !== -1) {
this.relevantChanges.splice(revertedCommitIndex, 1);
this.relevantChanges.splice(i, 1);
i--;
break;
}
}
}
}
if (this.isVersionPlans) {
for (let i = this.relevantChanges.length - 1; i >= 0; i--) {
if (this.relevantChanges[i].isBreaking) {
const change = this.relevantChanges[i];
this.additionalChangesForAuthorsSection.push(change);
const line = this.formatChange(change);
this.breakingChanges.push(line);
this.relevantChanges.splice(i, 1);
}
}
}
else {
for (const change of this.relevantChanges) {
if (change.isBreaking) {
this.breakingChanges.push(this.formatBreakingChange(change));
}
}
}
}
shouldRenderEmptyEntry() {
return (this.relevantChanges.length === 0 &&
this.breakingChanges.length === 0 &&
!this.hasDependencyBumps());
}
renderEmptyEntry() {
if (this.hasDependencyBumps()) {
return [
this.renderVersionTitle(),
'',
...this.renderDependencyBumps(),
].join('\n');
}
else if (this.entryWhenNoChanges) {
return `${this.renderVersionTitle()}\n\n${this.entryWhenNoChanges}`;
}
return '';
}
renderVersionTitle() {
let isMajorVersion = true;
try {
isMajorVersion =
`${(0, semver_1.major)(this.changelogEntryVersion)}.0.0` ===
this.changelogEntryVersion.replace(/^v/, '');
}
catch {
// Do nothing with the error
// Prevent non-semver versions from erroring out
}
let maybeDateStr = '';
if (this.changelogRenderOptions.versionTitleDate) {
const dateStr = new Date().toISOString().slice(0, 10);
maybeDateStr = ` (${dateStr})`;
}
return isMajorVersion
? `# ${this.changelogEntryVersion}${maybeDateStr}`
: `## ${this.changelogEntryVersion}${maybeDateStr}`;
}
renderChangesByType() {
const markdownLines = [];
const typeGroups = this.groupChangesByType();
const changeTypes = this.conventionalCommitsConfig.types;
for (const type of Object.keys(changeTypes)) {
const group = typeGroups[type];
if (!group || group.length === 0) {
continue;
}
markdownLines.push('', `### ${changeTypes[type].changelog.title}`, '');
if (this.project === null) {
const changesGroupedByScope = this.groupChangesByScope(group);
const scopesSortedAlphabetically = Object.keys(changesGroupedByScope).sort();
for (const scope of scopesSortedAlphabetically) {
const changes = changesGroupedByScope[scope];
for (const change of changes.reverse()) {
const line = this.formatChange(change);
markdownLines.push(line);
if (change.isBreaking && !this.isVersionPlans) {
this.breakingChanges.push(this.formatBreakingChange(change));
}
}
}
}
else {
// For project-specific changelogs, maintain the original order
for (const change of group) {
const line = this.formatChange(change);
markdownLines.push(line);
if (change.isBreaking && !this.isVersionPlans) {
this.breakingChanges.push(this.formatBreakingChange(change));
}
}
}
}
return markdownLines;
}
hasBreakingChanges() {
return this.breakingChanges.length > 0;
}
renderBreakingChanges() {
const uniqueBreakingChanges = Array.from(new Set(this.breakingChanges));
return ['### ⚠️ Breaking Changes', '', ...uniqueBreakingChanges];
}
hasDependencyBumps() {
return this.dependencyBumps && this.dependencyBumps.length > 0;
}
renderDependencyBumps() {
const markdownLines = ['', '### 🧱 Updated Dependencies', ''];
this.dependencyBumps.forEach(({ dependencyName, newVersion }) => {
markdownLines.push(`- Updated ${dependencyName} to ${newVersion}`);
});
return markdownLines;
}
shouldRenderAuthors() {
return this.changelogRenderOptions.authors;
}
async renderAuthors() {
const markdownLines = [];
const _authors = new Map();
for (const change of [
...this.relevantChanges,
...this.additionalChangesForAuthorsSection,
]) {
if (!change.authors) {
continue;
}
for (const author of change.authors) {
const name = this.formatName(author.name);
if (!name || name.includes('[bot]')) {
continue;
}
if (_authors.has(name)) {
const entry = _authors.get(name);
entry.email.add(author.email);
}
else {
_authors.set(name, { email: new Set([author.email]) });
}
}
}
if (this.remoteReleaseClient.getRemoteRepoData() &&
this.changelogRenderOptions.applyUsernameToAuthors &&
// TODO: Explore if it is possible to support GitLab username resolution
this.remoteReleaseClient.remoteReleaseProviderName === 'GitHub') {
await this.remoteReleaseClient.applyUsernameToAuthors(_authors);
}
const authors = [..._authors.entries()].map((e) => ({
name: e[0],
...e[1],
}));
if (authors.length > 0) {
markdownLines.push('', '### ' + '❤️ Thank You', '', ...authors
.sort((a, b) => a.name.localeCompare(b.name))
.map((i) => {
const username = i.username ? ` @${i.username}` : '';
return `- ${i.name}${username}`;
}));
}
return markdownLines;
}
formatChange(change) {
let description = change.description;
let extraLines = [];
let extraLinesStr = '';
if (description.includes('\n')) {
[description, ...extraLines] = description.split('\n');
const indentation = ' ';
extraLinesStr = (this.isVersionPlans
? // Preserve newlines for version plan sources to allow author to maintain maximum control over final contents
extraLines
: extraLines.filter((l) => l.trim().length > 0))
// Only add indentation to lines with content
.map((l) => (l.trim().length > 0 ? `${indentation}${l}` : ''))
.join('\n');
}
let changeLine = '- ' +
(!this.isVersionPlans && change.isBreaking ? '⚠️ ' : '') +
(!this.isVersionPlans && change.scope
? `**${change.scope.trim()}:** `
: '') +
description;
if (this.remoteReleaseClient.getRemoteRepoData() &&
this.changelogRenderOptions.commitReferences &&
change.githubReferences) {
changeLine += this.remoteReleaseClient.formatReferences(change.githubReferences);
}
if (extraLinesStr) {
changeLine += (this.isVersionPlans ? '\n' : '\n\n') + extraLinesStr;
}
return changeLine;
}
formatBreakingChangeBase(change) {
let breakingLine = '- ';
if (change.scope) {
breakingLine += `**${change.scope.trim()}:** `;
}
if (change.description) {
breakingLine += `${change.description.trim()}`;
}
if (this.remoteReleaseClient.getRemoteRepoData() &&
this.changelogRenderOptions.commitReferences &&
change.githubReferences) {
breakingLine += ` ${this.remoteReleaseClient.formatReferences(change.githubReferences)}`;
}
return breakingLine;
}
formatBreakingChange(change) {
const explanation = this.extractBreakingChangeExplanation(change.body);
const baseLine = this.formatBreakingChangeBase(change);
if (!explanation) {
return baseLine;
}
const indentation = ' ';
let breakingLine = baseLine + `\n${indentation}`;
// Handle multi-line explanations
let explanationText = explanation;
let extraLines = [];
if (explanation.includes('\n')) {
[explanationText, ...extraLines] = explanation.split('\n');
}
breakingLine += explanationText;
// Add extra lines with indentation (matching formatChange behavior)
if (extraLines.length > 0) {
const extraLinesStr = extraLines
.filter((l) => l.trim().length > 0)
.map((l) => `${indentation}${l}`)
.join('\n');
if (extraLinesStr) {
breakingLine += '\n' + extraLinesStr;
}
}
return breakingLine;
}
groupChangesByType() {
const typeGroups = {};
for (const change of this.relevantChanges) {
typeGroups[change.type] = typeGroups[change.type] || [];
typeGroups[change.type].push(change);
}
return typeGroups;
}
groupChangesByScope(changes) {
const scopeGroups = {};
for (const change of changes) {
const scope = change.scope || '';
scopeGroups[scope] = scopeGroups[scope] || [];
scopeGroups[scope].push(change);
}
return scopeGroups;
}
extractBreakingChangeExplanation(message) {
if (!message) {
return null;
}
const breakingChangeIdentifier = 'BREAKING CHANGE:';
const startIndex = message.indexOf(breakingChangeIdentifier);
if (startIndex === -1) {
return null;
}
const startOfBreakingChange = startIndex + breakingChangeIdentifier.length;
// Extract all text after BREAKING CHANGE: until we hit a Co-authored-by section or git metadata
let endOfBreakingChange = message.length;
const coAuthoredBySection = message.indexOf('---------\n\nCo-authored-by:');
if (coAuthoredBySection !== -1) {
endOfBreakingChange = coAuthoredBySection;
}
else {
// Look for the git metadata delimiter (a line with just ")
const gitMetadataMarker = message.indexOf('"\n', startOfBreakingChange);
if (gitMetadataMarker !== -1) {
endOfBreakingChange = gitMetadataMarker;
}
}
return message.substring(startOfBreakingChange, endOfBreakingChange).trim();
}
formatName(name = '') {
return name
.split(' ')
.map((p) => p.trim())
.join(' ');
}
}
exports.default = DefaultChangelogRenderer;

4
node_modules/nx/dist/release/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
/**
* @public Programmatic API for nx release
*/
export { ReleaseClient, release, releaseChangelog, releasePublish, releaseVersion, VersionActions, AfterAllProjectsVersioned, } from '../src/command-line/release';

13
node_modules/nx/dist/release/index.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VersionActions = exports.releaseVersion = exports.releasePublish = exports.releaseChangelog = exports.release = exports.ReleaseClient = void 0;
/**
* @public Programmatic API for nx release
*/
var release_1 = require("../src/command-line/release");
Object.defineProperty(exports, "ReleaseClient", { enumerable: true, get: function () { return release_1.ReleaseClient; } });
Object.defineProperty(exports, "release", { enumerable: true, get: function () { return release_1.release; } });
Object.defineProperty(exports, "releaseChangelog", { enumerable: true, get: function () { return release_1.releaseChangelog; } });
Object.defineProperty(exports, "releasePublish", { enumerable: true, get: function () { return release_1.releasePublish; } });
Object.defineProperty(exports, "releaseVersion", { enumerable: true, get: function () { return release_1.releaseVersion; } });
Object.defineProperty(exports, "VersionActions", { enumerable: true, get: function () { return release_1.VersionActions; } });

10
node_modules/nx/dist/src/adapter/angular-json.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
import { ProjectsConfigurations } from '../config/workspace-json-project-json';
import { NxPluginV2 } from '../project-graph/plugins';
export declare const NX_ANGULAR_JSON_PLUGIN_NAME = "nx-angular-json-plugin";
export declare const NxAngularJsonPlugin: NxPluginV2;
export default NxAngularJsonPlugin;
export declare function shouldMergeAngularProjects(root: string, includeProjectsFromAngularJson: boolean): boolean;
export declare function isAngularPluginInstalled(): boolean;
export declare function toNewFormat(w: any): ProjectsConfigurations;
export declare function toOldFormat(w: any): any;
export declare function renamePropertyWithStableKeys(obj: any, from: string, to: string): void;

130
node_modules/nx/dist/src/adapter/angular-json.js generated vendored Normal file
View File

@@ -0,0 +1,130 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NxAngularJsonPlugin = exports.NX_ANGULAR_JSON_PLUGIN_NAME = void 0;
exports.shouldMergeAngularProjects = shouldMergeAngularProjects;
exports.isAngularPluginInstalled = isAngularPluginInstalled;
exports.toNewFormat = toNewFormat;
exports.toOldFormat = toOldFormat;
exports.renamePropertyWithStableKeys = renamePropertyWithStableKeys;
const tslib_1 = require("tslib");
const fs_1 = require("fs");
const path = tslib_1.__importStar(require("path"));
const fileutils_1 = require("../utils/fileutils");
exports.NX_ANGULAR_JSON_PLUGIN_NAME = 'nx-angular-json-plugin';
const createNodes = [
'angular.json',
(f, _, ctx) => [
[
'angular.json',
{
projects: readAngularJson(ctx.workspaceRoot),
},
],
],
];
exports.NxAngularJsonPlugin = {
name: exports.NX_ANGULAR_JSON_PLUGIN_NAME,
createNodes,
createNodesV2: createNodes,
};
exports.default = exports.NxAngularJsonPlugin;
function shouldMergeAngularProjects(root, includeProjectsFromAngularJson) {
if ((0, fs_1.existsSync)(path.join(root, 'angular.json')) &&
// Include projects from angular.json if explicitly required.
// e.g. when invoked from `packages/devkit/src/utils/convert-nx-executor.ts`
(includeProjectsFromAngularJson ||
// Or if a workspace has `@nx/angular` installed then projects from `angular.json` to be considered by Nx.
isAngularPluginInstalled())) {
return true;
}
else {
return false;
}
}
function isAngularPluginInstalled() {
try {
// nx-ignore-next-line
require.resolve('@nx/angular');
return true;
}
catch {
return false;
}
}
function readAngularJson(angularCliWorkspaceRoot) {
return toNewFormat((0, fileutils_1.readJsonFile)(path.join(angularCliWorkspaceRoot, 'angular.json'))).projects;
}
function toNewFormat(w) {
if (!w.projects) {
return w;
}
for (const name in w.projects ?? {}) {
const projectConfig = w.projects[name];
if (projectConfig.architect) {
renamePropertyWithStableKeys(projectConfig, 'architect', 'targets');
}
if (projectConfig.schematics) {
renamePropertyWithStableKeys(projectConfig, 'schematics', 'generators');
}
if (!projectConfig.name) {
projectConfig.name = name;
}
Object.values(projectConfig.targets || {}).forEach((target) => {
if (target.builder !== undefined) {
renamePropertyWithStableKeys(target, 'builder', 'executor');
}
});
}
if (w.schematics) {
renamePropertyWithStableKeys(w, 'schematics', 'generators');
}
if (w.version !== 2) {
w.version = 2;
}
return w;
}
function toOldFormat(w) {
if (w.projects) {
for (const name in w.projects) {
const projectConfig = w.projects[name];
if (typeof projectConfig === 'string') {
throw new Error("'project.json' files are incompatible with version 1 workspace schemas.");
}
if (projectConfig.targets) {
renamePropertyWithStableKeys(projectConfig, 'targets', 'architect');
}
if (projectConfig.generators) {
renamePropertyWithStableKeys(projectConfig, 'generators', 'schematics');
}
delete projectConfig.name;
Object.values(projectConfig.architect || {}).forEach((target) => {
if (target.executor !== undefined) {
renamePropertyWithStableKeys(target, 'executor', 'builder');
}
});
}
}
if (w.generators) {
renamePropertyWithStableKeys(w, 'generators', 'schematics');
}
if (w.version !== 1) {
w.version = 1;
}
return w;
}
// we have to do it this way to preserve the order of properties
// not to screw up the formatting
function renamePropertyWithStableKeys(obj, from, to) {
const copy = { ...obj };
Object.keys(obj).forEach((k) => {
delete obj[k];
});
Object.keys(copy).forEach((k) => {
if (k === from) {
obj[to] = copy[k];
}
else {
obj[k] = copy[k];
}
});
}

2
node_modules/nx/dist/src/adapter/compat.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export declare const allowedProjectExtensions: readonly ["tags", "implicitDependencies", "configFilePath", "$schema", "generators", "namedInputs", "name", "files", "root", "sourceRoot", "projectType", "release", "includedScripts", "metadata", "owners", "nxCloudImplicitDependencies"];
export declare const allowedWorkspaceExtensions: readonly ["$schema", "implicitDependencies", "affected", "defaultBase", "tasksRunnerOptions", "workspaceLayout", "plugins", "targetDefaults", "files", "generators", "namedInputs", "extends", "cli", "pluginsConfig", "defaultProject", "installation", "release", "nxCloudAccessToken", "nxCloudId", "nxCloudUrl", "nxCloudEncryptionKey", "parallel", "cacheDirectory", "useDaemonProcess", "useInferencePlugins", "neverConnectToCloud", "analytics", "sync", "useLegacyCache", "maxCacheSize", "tui", "owners"];

153
node_modules/nx/dist/src/adapter/compat.js generated vendored Normal file
View File

@@ -0,0 +1,153 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.allowedWorkspaceExtensions = exports.allowedProjectExtensions = void 0;
const project_graph_1 = require("../project-graph/project-graph");
const configuration_1 = require("../config/configuration");
const angular_json_1 = require("./angular-json");
const Module = require('module');
const originalRequire = Module.prototype.require;
let patched = false;
// If we pass props on a project that angular doesn't know about,
// it throws a warning that users see. We want to pass them still,
// so older plugins writtin in Ng Devkit can update these.
//
// 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.
exports.allowedProjectExtensions = [
'tags',
'implicitDependencies',
'configFilePath',
'$schema',
'generators',
'namedInputs',
'name',
'files',
'root',
'sourceRoot',
'projectType',
'release',
'includedScripts',
'metadata',
'owners',
'nxCloudImplicitDependencies',
];
// If we pass props on the workspace that angular doesn't know about,
// it throws a warning that users see. We want to pass them still,
// so older plugins writtin in Ng Devkit can update these.
//
// 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.
exports.allowedWorkspaceExtensions = [
'$schema',
'implicitDependencies',
'affected',
'defaultBase',
'tasksRunnerOptions',
'workspaceLayout',
'plugins',
'targetDefaults',
'files',
'generators',
'namedInputs',
'extends',
'cli',
'pluginsConfig',
'defaultProject',
'installation',
'release',
'nxCloudAccessToken',
'nxCloudId',
'nxCloudUrl',
'nxCloudEncryptionKey',
'parallel',
'cacheDirectory',
'useDaemonProcess',
'useInferencePlugins',
'neverConnectToCloud',
'analytics',
'sync',
'useLegacyCache',
'maxCacheSize',
'tui',
'owners',
];
if (!patched) {
Module.prototype.require = function () {
const result = originalRequire.apply(this, arguments);
if (arguments[0].startsWith('@angular-devkit/core')) {
const ngCoreWorkspace = originalRequire.apply(this, [
`@angular-devkit/core/src/workspace/core`,
]);
mockReadWorkspace(ngCoreWorkspace);
const readJsonUtils = originalRequire.apply(this, [
`@angular-devkit/core/src/workspace/json/reader`,
]);
mockReadJsonWorkspace(readJsonUtils);
}
return result;
};
try {
require('@angular-devkit/build-angular/src/utils/version').Version.assertCompatibleAngularVersion =
() => { };
}
catch (e) { }
try {
require('@angular-devkit/build-angular/src/utils/version').assertCompatibleAngularVersion =
() => { };
}
catch (e) { }
try {
require('@angular/build/private').assertCompatibleAngularVersion = () => { };
}
catch (e) { }
patched = true;
}
function mockReadWorkspace(ngCoreWorkspace) {
mockMember(ngCoreWorkspace, 'readWorkspace', (originalReadWorkspace) => (path, ...rest) => {
path = 'angular.json';
return originalReadWorkspace.apply(this, [path, ...rest]);
});
}
/**
* Patch readJsonWorkspace to handle workspaces without a central workspace file.
* NOTE: We hide warnings that would be logged during this process.
*/
function mockReadJsonWorkspace(readJsonUtils) {
mockMember(readJsonUtils, 'readJsonWorkspace', (originalReadJsonWorkspace) => async (path, host, options) => {
const modifiedOptions = {
...options,
allowedProjectExtensions: exports.allowedProjectExtensions,
allowedWorkspaceExtensions: exports.allowedWorkspaceExtensions,
};
try {
// Attempt angular CLI default behaviour
return await originalReadJsonWorkspace(path, host, modifiedOptions);
}
catch {
// This failed. Its most likely due to a lack of a workspace definition file,
// or other things that are different between NgCLI and Nx config files.
const projectGraph = await (0, project_graph_1.createProjectGraphAsync)();
const nxJson = (0, configuration_1.readNxJson)();
// Construct old workspace.json format from project graph
const w = {
...nxJson,
...(0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph),
};
// Read our v1 workspace schema
const workspaceConfiguration = (0, angular_json_1.toOldFormat)(w);
// readJsonWorkspace actually has AST parsing + more, so we
// still need to call it rather than just return our file
return originalReadJsonWorkspace.apply(this, [
'angular.json', // path name, doesn't matter
{
// second arg is a host, only method used is readFile
readFile: () => JSON.stringify(workspaceConfiguration),
},
modifiedOptions,
]);
}
});
}
function mockMember(obj, method, factory) {
obj[method] = factory(obj[method]);
}

104
node_modules/nx/dist/src/adapter/ngcli-adapter.d.ts generated vendored Normal file
View File

@@ -0,0 +1,104 @@
import { logging, Path, PathFragment, virtualFs } from '@angular-devkit/core';
import { FileBuffer } from '@angular-devkit/core/src/virtual-fs/host/interface';
import { Observable } from 'rxjs';
import type { GenerateOptions } from '../command-line/generate/generate';
import { ProjectConfiguration } from '../config/workspace-json-project-json';
import { Tree } from '../generators/tree';
import type { ProjectGraph } from '../config/project-graph';
import { ExecutorContext, GeneratorCallback } from '../config/misc-interfaces';
export declare function createBuilderContext(builderInfo: {
builderName: string;
description: string;
optionSchema: any;
}, context: ExecutorContext): Promise<import("@angular-devkit/architect").BuilderContext>;
export declare function scheduleTarget(root: string, opts: {
project: string;
target: string;
configuration: string;
runOptions: any;
projects: Record<string, ProjectConfiguration>;
}, verbose: boolean, projectGraph: ProjectGraph): Promise<Observable<import('@angular-devkit/architect').BuilderOutput>>;
type AngularProjectConfiguration = ProjectConfiguration & {
prefix?: string;
};
export declare class NxScopedHost extends virtualFs.ScopedHost<any> {
private root;
protected _projectGraph?: ProjectGraph;
constructor(root: string, _projectGraph?: ProjectGraph);
read(path: Path): Observable<FileBuffer>;
protected readMergedWorkspaceConfiguration(): Observable<any>;
write(path: Path, content: FileBuffer): Observable<void>;
isFile(path: Path): Observable<boolean>;
exists(path: Path): Observable<boolean>;
mergeProjectConfiguration(existing: AngularProjectConfiguration, updated: AngularProjectConfiguration, projectName: string): AngularProjectConfiguration;
readExistingAngularJson(): Observable<any>;
protected readJson<T = any>(path: string): Observable<T>;
}
/**
* Host used by Angular CLI builders. It reads the project configurations from
* the project graph to access the expanded targets.
*/
export declare class NxScopedHostForBuilders extends NxScopedHost {
constructor(root: string, projectGraph: ProjectGraph);
protected readMergedWorkspaceConfiguration(): Observable<any>;
}
export declare function arrayBufferToString(buffer: any): string;
/**
* Host used by Angular CLI schematics. It reads the project configurations from
* the project configuration files.
*/
export declare class NxScopeHostUsedForWrappedSchematics extends NxScopedHost {
private readonly host;
constructor(root: string, host: Tree, projectGraph: ProjectGraph);
read(path: Path): Observable<FileBuffer>;
exists(path: Path): Observable<boolean>;
isDirectory(path: Path): Observable<boolean>;
isFile(path: Path): Observable<boolean>;
list(path: Path): Observable<PathFragment[]>;
}
export declare function generate(root: string, opts: GenerateOptions, projects: Record<string, ProjectConfiguration>, verbose: boolean, projectGraph: ProjectGraph): Promise<number>;
export declare function runMigration(root: string, packageName: string, migrationName: string, projects: Record<string, ProjectConfiguration>, isVerbose: boolean, projectGraph: ProjectGraph): Promise<{
loggingQueue: string[];
madeChanges: boolean;
}>;
/**
* If you have an Nx Devkit generator invoking the wrapped Angular Devkit schematic,
* and you don't want the Angular Devkit schematic to run, you can mock it up using this function.
*
* Unfortunately, there are some edge cases in the Nx-Angular devkit integration that
* can be seen in the unit tests context. This function is useful for handling that as well.
*
* In this case, you can mock it up.
*
* Example:
*
* ```typescript
* mockSchematicsForTesting({
* 'mycollection:myschematic': (tree, params) => {
* tree.write("README.md");
* }
* });
*
* ```
*/
export declare function mockSchematicsForTesting(schematics: {
[name: string]: (host: Tree, generatorOptions: {
[k: string]: any;
}) => Promise<void>;
}): void;
export declare function wrapAngularDevkitSchematic(collectionName: string, generatorName: string): (host: Tree, generatorOptions: {
[k: string]: any;
}) => Promise<GeneratorCallback>;
export declare const getLogger: (isVerbose?: boolean) => logging.Logger;
/**
* Restores Nx tokens in options when possible by comparing new and previous
* options.
* The function preserves tokens in the following cases:
* 1. When the resolved previous value matches the new value exactly
* 2. When the previous value used {workspaceRoot}
* 3. When the previous value used {projectRoot} and the new value starts with
* the project root path
* Those are the only safe cases, for all other cases, the new value is used as-is.
*/
export declare function restoreNxTokensInOptions<T extends Object | Array<unknown>>(newOptions: T, previousOptions: T, project: ProjectConfiguration): T;
export {};

1001
node_modules/nx/dist/src/adapter/ngcli-adapter.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

109
node_modules/nx/dist/src/adapter/rxjs-for-await.d.ts generated vendored Normal file
View File

@@ -0,0 +1,109 @@
import { Observable } from 'rxjs';
export declare class Deferred<T> {
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
promise: Promise<T>;
}
/**
* Will subscribe to the `source` observable provided,
*
* Allowing a `for await..of` loop to iterate over every
* value that the source emits.
*
* **WARNING**: If the async loop is slower than the observable
* producing values, the values will build up in a buffer
* and you could experience an out of memory error.
*
* This is a lossless subscription method. No value
* will be missed or duplicated.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const value of eachValueFrom(source$)) {
* console.log(value);
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
export declare function eachValueFrom<T>(source: Observable<T>): AsyncIterableIterator<T>;
/**
* Will subscribe to the `source` observable provided
* and build the emitted values up in a buffer. Allowing
* `for await..of` loops to iterate and get the buffer
* on each loop.
*
* This is a lossless subscription method. No value
* will be missed or duplicated.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const buffer of bufferedValuesFrom(source$)) {
* for (const value of buffer) {
* console.log(value);
* }
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
export declare function bufferedValuesFrom<T>(source: Observable<T>): AsyncGenerator<any, void, unknown>;
/**
* Will subscribe to the provided `source` observable,
* allowing `for await..of` loops to iterate and get the
* most recent value that was emitted. Will not iterate out
* the same emission twice.
*
* This is a lossy subscription method. Do not use if
* every value is important.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const value of latestValueFrom(source$)) {
* console.log(value);
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
export declare function latestValueFrom<T>(source: Observable<T>): AsyncGenerator<any, void, unknown>;
/**
* Subscribes to the provided `source` observable and allows
* `for await..of` loops to iterate over it, such that
* all values are dropped until the iteration occurs, then
* the very next value that arrives is provided to the
* `for await` loop.
*
* This is a lossy subscription method. Do not use if
* every value is important.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const value of nextValueFrom(source$)) {
* console.log(value);
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
export declare function nextValueFrom<T>(source: Observable<T>): AsyncGenerator<T, void, void>;

362
node_modules/nx/dist/src/adapter/rxjs-for-await.js generated vendored Normal file
View File

@@ -0,0 +1,362 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Deferred = void 0;
exports.eachValueFrom = eachValueFrom;
exports.bufferedValuesFrom = bufferedValuesFrom;
exports.latestValueFrom = latestValueFrom;
exports.nextValueFrom = nextValueFrom;
class Deferred {
constructor() {
this.resolve = null;
this.reject = null;
this.promise = new Promise((a, b) => {
this.resolve = a;
this.reject = b;
});
}
}
exports.Deferred = Deferred;
const RESOLVED = Promise.resolve();
/**
* Will subscribe to the `source` observable provided,
*
* Allowing a `for await..of` loop to iterate over every
* value that the source emits.
*
* **WARNING**: If the async loop is slower than the observable
* producing values, the values will build up in a buffer
* and you could experience an out of memory error.
*
* This is a lossless subscription method. No value
* will be missed or duplicated.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const value of eachValueFrom(source$)) {
* console.log(value);
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
async function* eachValueFrom(source) {
const deferreds = [];
const values = [];
let hasError = false;
let error = null;
let completed = false;
const subs = source.subscribe({
next: (value) => {
if (deferreds.length > 0) {
deferreds.shift().resolve({ value, done: false });
}
else {
values.push(value);
}
},
error: (err) => {
hasError = true;
error = err;
while (deferreds.length > 0) {
deferreds.shift().reject(err);
}
},
complete: () => {
completed = true;
while (deferreds.length > 0) {
deferreds.shift().resolve({ value: undefined, done: true });
}
},
});
try {
while (true) {
if (values.length > 0) {
yield values.shift();
}
else if (completed) {
return;
}
else if (hasError) {
throw error;
}
else {
const d = new Deferred();
deferreds.push(d);
const result = await d.promise;
if (result.done) {
return;
}
else {
yield result.value;
}
}
}
}
catch (err) {
throw err;
}
finally {
subs.unsubscribe();
}
}
/**
* Will subscribe to the `source` observable provided
* and build the emitted values up in a buffer. Allowing
* `for await..of` loops to iterate and get the buffer
* on each loop.
*
* This is a lossless subscription method. No value
* will be missed or duplicated.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const buffer of bufferedValuesFrom(source$)) {
* for (const value of buffer) {
* console.log(value);
* }
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
async function* bufferedValuesFrom(source) {
let deferred = null;
const buffer = [];
let hasError = false;
let error = null;
let completed = false;
const subs = source.subscribe({
next: (value) => {
if (deferred) {
deferred.resolve(RESOLVED.then(() => {
const bufferCopy = buffer.slice();
buffer.length = 0;
return { value: bufferCopy, done: false };
}));
deferred = null;
}
buffer.push(value);
},
error: (err) => {
hasError = true;
error = err;
if (deferred) {
deferred.reject(err);
deferred = null;
}
},
complete: () => {
completed = true;
if (deferred) {
deferred.resolve({ value: undefined, done: true });
deferred = null;
}
},
});
try {
while (true) {
if (buffer.length > 0) {
const bufferCopy = buffer.slice();
buffer.length = 0;
yield bufferCopy;
}
else if (completed) {
return;
}
else if (hasError) {
throw error;
}
else {
deferred = new Deferred();
const result = await deferred.promise;
if (result.done) {
return;
}
else {
yield result.value;
}
}
}
}
catch (err) {
throw err;
}
finally {
subs.unsubscribe();
}
}
/**
* Will subscribe to the provided `source` observable,
* allowing `for await..of` loops to iterate and get the
* most recent value that was emitted. Will not iterate out
* the same emission twice.
*
* This is a lossy subscription method. Do not use if
* every value is important.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const value of latestValueFrom(source$)) {
* console.log(value);
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
async function* latestValueFrom(source) {
let deferred = undefined;
let latestValue;
let hasLatestValue = false;
let hasError = false;
let error = null;
let completed = false;
const subs = source.subscribe({
next: (value) => {
hasLatestValue = true;
latestValue = value;
if (deferred) {
deferred.resolve(RESOLVED.then(() => {
hasLatestValue = false;
return { value: latestValue, done: false };
}));
}
},
error: (err) => {
hasError = true;
error = err;
if (deferred) {
deferred.reject(err);
}
},
complete: () => {
completed = true;
if (deferred) {
hasLatestValue = false;
deferred.resolve({ value: undefined, done: true });
}
},
});
try {
while (true) {
if (hasLatestValue) {
await RESOLVED;
const value = latestValue;
hasLatestValue = false;
yield value;
}
else if (completed) {
return;
}
else if (hasError) {
throw error;
}
else {
deferred = new Deferred();
const result = await deferred.promise;
if (result.done) {
return;
}
else {
yield result.value;
}
}
}
}
catch (err) {
throw err;
}
finally {
subs.unsubscribe();
}
}
/**
* Subscribes to the provided `source` observable and allows
* `for await..of` loops to iterate over it, such that
* all values are dropped until the iteration occurs, then
* the very next value that arrives is provided to the
* `for await` loop.
*
* This is a lossy subscription method. Do not use if
* every value is important.
*
* Example usage:
*
* ```ts
* async function test() {
* const source$ = getSomeObservable();
*
* for await(const value of nextValueFrom(source$)) {
* console.log(value);
* }
* }
* ```
*
* @param source the Observable source to await values from
*/
async function* nextValueFrom(source) {
let deferred = undefined;
let hasError = false;
let error = null;
let completed = false;
const subs = source.subscribe({
next: (value) => {
if (deferred) {
deferred.resolve({ value, done: false });
}
},
error: (err) => {
hasError = true;
error = err;
if (deferred) {
deferred.reject(err);
}
},
complete: () => {
completed = true;
if (deferred) {
deferred.resolve({ value: undefined, done: true });
}
},
});
try {
while (true) {
if (completed) {
return;
}
else if (hasError) {
throw error;
}
else {
deferred = new Deferred();
const result = await deferred.promise;
if (result.done) {
return;
}
else {
yield result.value;
}
}
}
}
catch (err) {
throw err;
}
finally {
subs.unsubscribe();
}
}

11
node_modules/nx/dist/src/ai/clone-ai-config-repo.d.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
/**
* Get the path to the cached nx-ai-agents-config repository.
* Uses a commit-hash based caching strategy:
* 1. Fetches the latest commit hash from the remote repository
* 2. Checks if a cached version exists for that hash
* 3. If not, clones the repository and cleans up old caches
*
* @returns The path to the cached repository
* @throws Error if unable to fetch or clone the repository
*/
export declare function getAiConfigRepoPath(): string;

155
node_modules/nx/dist/src/ai/clone-ai-config-repo.js generated vendored Normal file
View File

@@ -0,0 +1,155 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAiConfigRepoPath = getAiConfigRepoPath;
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const os_1 = require("os");
const path_1 = require("path");
const REPO_URL = 'https://github.com/nrwl/nx-ai-agents-config';
const CACHE_DIR = (0, path_1.join)((0, os_1.tmpdir)(), 'nx-ai-agents-config');
/**
* Get the latest commit hash from the remote repository.
* Uses `git ls-remote` to fetch the HEAD commit hash without cloning.
*/
function getLatestCommitHash() {
try {
const output = (0, child_process_1.execSync)(`git ls-remote ${REPO_URL} HEAD`, {
encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
timeout: 30000, // 30 second timeout
windowsHide: true,
});
const hash = output.split('\t')[0];
if (!hash || hash.length < 10) {
throw new Error('Invalid commit hash received');
}
// Return first 10 characters of the commit hash
return hash.substring(0, 10);
}
catch (error) {
throw new Error(`Failed to fetch latest commit hash from ${REPO_URL}. Please check your network connection.`);
}
}
/**
* Clone the repository to the specified path using shallow clone.
*/
function cloneRepo(targetPath) {
try {
// Ensure parent directory exists
(0, fs_1.mkdirSync)(CACHE_DIR, { recursive: true });
// Use a temporary path first to avoid race conditions
const tempPath = `${targetPath}.tmp.${process.pid}`;
// Clean up any leftover temp directory
if ((0, fs_1.existsSync)(tempPath)) {
(0, fs_1.rmSync)(tempPath, { recursive: true, force: true });
}
(0, child_process_1.execSync)(`git clone --depth 1 ${REPO_URL} "${tempPath}"`, {
encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
timeout: 120000, // 2 minute timeout for clone
windowsHide: true,
});
// Remove .git directory after clone
const gitDir = (0, path_1.join)(tempPath, '.git');
if ((0, fs_1.existsSync)(gitDir)) {
(0, fs_1.rmSync)(gitDir, { recursive: true, force: true });
}
// Atomically move temp directory to final location
// If targetPath already exists (race condition), just clean up temp
if ((0, fs_1.existsSync)(targetPath)) {
(0, fs_1.rmSync)(tempPath, { recursive: true, force: true });
}
else {
// Rename is atomic on the same filesystem
try {
(0, fs_1.renameSync)(tempPath, targetPath);
}
catch {
// Rename failed - check if another process won the race
if ((0, fs_1.existsSync)(targetPath)) {
// Another process created it, clean up our temp
(0, fs_1.rmSync)(tempPath, { recursive: true, force: true });
}
else {
// targetPath still doesn't exist - retry once
try {
(0, fs_1.renameSync)(tempPath, targetPath);
}
catch (retryError) {
// Clean up and fail
(0, fs_1.rmSync)(tempPath, { recursive: true, force: true });
throw new Error(`Failed to move cloned repository to cache location: ${retryError.message}`);
}
}
}
}
}
catch (error) {
// Re-throw if it's already our error (from rename failure)
if (error instanceof Error && error.message.startsWith('Failed to move')) {
throw error;
}
throw new Error(`Failed to clone ${REPO_URL}. Please check your network connection.`);
}
}
/**
* Clean up old cached versions, keeping only the current one.
*/
function cleanupOldCaches(currentCommitHash) {
if (!(0, fs_1.existsSync)(CACHE_DIR)) {
return;
}
try {
const entries = (0, fs_1.readdirSync)(CACHE_DIR, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name !== currentCommitHash) {
const oldCachePath = (0, path_1.join)(CACHE_DIR, entry.name);
(0, fs_1.rmSync)(oldCachePath, { recursive: true, force: true });
}
}
}
catch {
// Ignore cleanup errors - not critical
}
}
/**
* Get the path to the cached nx-ai-agents-config repository.
* Uses a commit-hash based caching strategy:
* 1. Fetches the latest commit hash from the remote repository
* 2. Checks if a cached version exists for that hash
* 3. If not, clones the repository and cleans up old caches
*
* @returns The path to the cached repository
* @throws Error if unable to fetch or clone the repository
*/
function getAiConfigRepoPath() {
// 1. Get latest commit hash (first 10 chars)
const commitHash = getLatestCommitHash();
// 2. Reuse cached version if it still has content (macOS may have
// swept its files but left the directory tree).
const cachedPath = (0, path_1.join)(CACHE_DIR, commitHash);
if (hasRootFile(cachedPath)) {
return cachedPath;
}
// 3. Wipe any empty skeleton, then clone fresh
if ((0, fs_1.existsSync)(cachedPath)) {
(0, fs_1.rmSync)(cachedPath, { recursive: true, force: true });
}
cloneRepo(cachedPath);
// 4. Clean up old cached versions
cleanupOldCaches(commitHash);
return cachedPath;
}
/**
* The repo always has at least one regular file at its root (e.g. README).
* If everything at the root is a directory, the cache was swept by macOS
* tmp cleanup and we should re-clone.
*/
function hasRootFile(dir) {
try {
return (0, fs_1.readdirSync)(dir, { withFileTypes: true }).some((e) => e.isFile());
}
catch {
return false;
}
}

26
node_modules/nx/dist/src/ai/constants.d.ts generated vendored Normal file
View File

@@ -0,0 +1,26 @@
import { AgentRulesOptions } from './set-up-ai-agents/get-agent-rules';
export type { AgentRulesOptions };
export declare function agentsMdPath(root: string): string;
export declare function geminiMdPath(root: string): string;
export declare function parseGeminiSettings(root: string): any | undefined;
export declare function geminiSettingsPath(root: string): string;
export declare function claudeMdPath(root: string): string;
export declare function claudeMcpJsonPath(root: string): string;
export declare function opencodeMcpPath(root: string): string;
export declare function codexConfigTomlPath(root: string): string;
export declare const nxRulesMarkerCommentStart = "<!-- nx configuration start-->";
export declare const nxRulesMarkerCommentDescription = "<!-- Leave the start & end comments to automatically receive updates. -->";
export declare const nxRulesMarkerCommentEnd = "<!-- nx configuration end-->";
export declare const rulesRegex: RegExp;
export interface AgentRulesWrappedOptions {
writeNxCloudRules: boolean;
useH1?: boolean;
}
export declare const getAgentRulesWrapped: (options: AgentRulesWrappedOptions) => string;
export declare const nxMcpTomlHeader = "[mcp_servers.\"nx-mcp\"]";
/**
* Get the MCP TOML configuration based on the Nx version.
* For Nx 22+, uses 'nx mcp'
* For Nx < 22, uses 'nx-mcp'
*/
export declare function getNxMcpTomlConfig(nxVersion: string): string;

71
node_modules/nx/dist/src/ai/constants.js generated vendored Normal file
View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nxMcpTomlHeader = exports.getAgentRulesWrapped = exports.rulesRegex = exports.nxRulesMarkerCommentEnd = exports.nxRulesMarkerCommentDescription = exports.nxRulesMarkerCommentStart = void 0;
exports.agentsMdPath = agentsMdPath;
exports.geminiMdPath = geminiMdPath;
exports.parseGeminiSettings = parseGeminiSettings;
exports.geminiSettingsPath = geminiSettingsPath;
exports.claudeMdPath = claudeMdPath;
exports.claudeMcpJsonPath = claudeMcpJsonPath;
exports.opencodeMcpPath = opencodeMcpPath;
exports.codexConfigTomlPath = codexConfigTomlPath;
exports.getNxMcpTomlConfig = getNxMcpTomlConfig;
const path_1 = require("path");
const semver_1 = require("semver");
const fileutils_1 = require("../utils/fileutils");
const get_agent_rules_1 = require("./set-up-ai-agents/get-agent-rules");
function agentsMdPath(root) {
return (0, path_1.join)(root, 'AGENTS.md');
}
function geminiMdPath(root) {
return (0, path_1.join)(root, 'GEMINI.md');
}
function parseGeminiSettings(root) {
const settingsPath = geminiSettingsPath(root);
try {
return (0, fileutils_1.readJsonFile)(settingsPath);
}
catch {
return undefined;
}
}
function geminiSettingsPath(root) {
return (0, path_1.join)(root, '.gemini', 'settings.json');
}
function claudeMdPath(root) {
return (0, path_1.join)(root, 'CLAUDE.md');
}
function claudeMcpJsonPath(root) {
return (0, path_1.join)(root, '.mcp.json');
}
function opencodeMcpPath(root) {
return (0, path_1.join)(root, 'opencode.json');
}
function codexConfigTomlPath(root) {
return (0, path_1.join)(root, '.codex', 'config.toml');
}
exports.nxRulesMarkerCommentStart = `<!-- nx configuration start-->`;
exports.nxRulesMarkerCommentDescription = `<!-- Leave the start & end comments to automatically receive updates. -->`;
exports.nxRulesMarkerCommentEnd = `<!-- nx configuration end-->`;
exports.rulesRegex = new RegExp(`${exports.nxRulesMarkerCommentStart}[\\s\\S]*?${exports.nxRulesMarkerCommentEnd}`, 'm');
const getAgentRulesWrapped = (options) => {
const { writeNxCloudRules, useH1 = true } = options;
const agentRulesString = (0, get_agent_rules_1.getAgentRules)({ nxCloud: writeNxCloudRules, useH1 });
return `${exports.nxRulesMarkerCommentStart}\n${exports.nxRulesMarkerCommentDescription}\n\n${agentRulesString}\n\n${exports.nxRulesMarkerCommentEnd}`;
};
exports.getAgentRulesWrapped = getAgentRulesWrapped;
exports.nxMcpTomlHeader = `[mcp_servers."nx-mcp"]`;
/**
* Get the MCP TOML configuration based on the Nx version.
* For Nx 22+, uses 'nx mcp'
* For Nx < 22, uses 'nx-mcp'
*/
function getNxMcpTomlConfig(nxVersion) {
const majorVersion = (0, semver_1.major)(nxVersion);
const args = majorVersion >= 22 ? '["nx", "mcp"]' : '["nx-mcp"]';
return `${exports.nxMcpTomlHeader}
type = "stdio"
command = "npx"
args = ${args}
`;
}

2
node_modules/nx/dist/src/ai/detect-ai-agent.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import { Agent } from './utils';
export declare function detectAiAgent(): Agent | null;

12
node_modules/nx/dist/src/ai/detect-ai-agent.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectAiAgent = detectAiAgent;
const native_1 = require("../native");
const utils_1 = require("./utils");
function detectAiAgent() {
const detected = (0, native_1.detectAiAgent)();
if (detected && utils_1.supportedAgents.includes(detected)) {
return detected;
}
return null;
}

View File

@@ -0,0 +1,5 @@
export interface AgentRulesOptions {
nxCloud: boolean;
useH1?: boolean;
}
export declare function getAgentRules(options: AgentRulesOptions): string;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAgentRules = getAgentRules;
function getAgentRules(options) {
const { nxCloud, useH1 = true } = options;
const header = useH1 ? '#' : '##';
return `${header} General Guidelines for working with Nx
- For navigating/exploring the workspace, invoke the \`nx-workspace\` skill first - it has patterns for querying projects, targets, and dependencies
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through \`nx\` (i.e. \`nx run\`, \`nx run-many\`, \`nx affected\`) instead of using the underlying tooling directly
- Prefix nx commands with the workspace's package manager (e.g., \`pnpm nx build\`, \`npm exec nx test\`) - avoids using globally installed CLI
- You have access to the Nx MCP server and its tools, use them to help the user
- For Nx plugin best practices, check \`node_modules/@nx/<plugin>/PLUGIN.md\`. Not all plugins have this file - proceed without it if unavailable.
- NEVER guess CLI flags - always check nx_docs or \`--help\` first when unsure
## Scaffolding & Generators
- For scaffolding tasks (creating apps, libs, project structure, setup), ALWAYS invoke the \`nx-generate\` skill FIRST before exploring or calling MCP tools
## When to use nx_docs
- USE for: advanced config options, unfamiliar flags, migration guides, plugin configuration, edge cases
- DON'T USE for: basic generator syntax (\`nx g @nx/react:app\`), standard commands, things you already know
- The \`nx-generate\` skill handles generator discovery internally - don't call nx_docs just to look up generator syntax
`;
}

View File

@@ -0,0 +1,11 @@
import type { Agent } from '../utils';
export type SetupAiAgentsGeneratorSchema = {
directory: string;
writeNxCloudRules?: boolean;
packageVersion?: string;
agents?: Agent[];
};
export type NormalizedSetupAiAgentsGeneratorSchema =
Required<SetupAiAgentsGeneratorSchema>;

View File

@@ -0,0 +1,34 @@
{
"$schema": "https://json-schema.org/schema",
"$id": "SetupAiAgents",
"title": "Set Up AI Agents",
"description": "Sets up the Nx MCP & rule files for common AI Agents.",
"type": "object",
"properties": {
"directory": {
"type": "string",
"description": "Directory where the AI agent configuration files will be generated",
"default": "."
},
"writeNxCloudRules": {
"type": "boolean",
"description": "Whether to write Nx Cloud rules",
"default": false
},
"packageVersion": {
"type": "string",
"description": "The version of the package to use",
"default": "latest"
},
"agents": {
"type": "array",
"description": "The agents to setup Nx configuration for.",
"items": {
"type": "string",
"enum": ["claude", "gemini", "codex", "cursor", "copilot", "opencode"]
},
"default": ["claude", "gemini", "codex", "cursor", "copilot", "opencode"]
}
},
"required": ["directory"]
}

View File

@@ -0,0 +1,10 @@
import { Tree } from '../../generators/tree';
import { CLIErrorMessageConfig, CLINoteMessageConfig } from '../../utils/output';
import { NormalizedSetupAiAgentsGeneratorSchema, SetupAiAgentsGeneratorSchema } from './schema';
export type ModificationResults = {
messages: CLINoteMessageConfig[];
errors: CLIErrorMessageConfig[];
};
export declare function setupAiAgentsGenerator(tree: Tree, options: SetupAiAgentsGeneratorSchema, inner?: boolean): Promise<(check?: boolean) => Promise<ModificationResults>>;
export declare function setupAiAgentsGeneratorImpl(tree: Tree, options: NormalizedSetupAiAgentsGeneratorSchema): Promise<() => Promise<ModificationResults>>;
export default setupAiAgentsGenerator;

View File

@@ -0,0 +1,516 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupAiAgentsGenerator = setupAiAgentsGenerator;
exports.setupAiAgentsGeneratorImpl = setupAiAgentsGeneratorImpl;
const tslib_1 = require("tslib");
const fs_1 = require("fs");
const path_1 = require("path");
const semver_1 = require("semver");
const smol_toml_1 = tslib_1.__importDefault(require("smol-toml"));
const format_changed_files_with_prettier_if_available_1 = require("../../generators/internal-utils/format-changed-files-with-prettier-if-available");
const generate_files_1 = require("../../generators/utils/generate-files");
const json_1 = require("../../generators/utils/json");
const native_1 = require("../../native");
const package_json_1 = require("../../utils/package-json");
const ignore_1 = require("../../utils/ignore");
const provenance_1 = require("../../utils/provenance");
const workspace_root_1 = require("../../utils/workspace-root");
const installed_nx_version_1 = require("../../utils/installed-nx-version");
const constants_1 = require("../constants");
const clone_ai_config_repo_1 = require("../clone-ai-config-repo");
const utils_1 = require("../utils");
const handle_import_1 = require("../../utils/handle-import");
/**
* Best-effort fallback when `getInstalledNxVersion()` can't find an
* installed nx — read the version declared in the workspace's
* `package.json` (devDependencies/dependencies), stripping any semver
* range prefix, and finally a sane default.
*/
function getDeclaredNxVersionOrDefault() {
try {
const workspacePackageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(workspace_root_1.workspaceRoot, 'package.json'), 'utf-8'));
const declared = workspacePackageJson.devDependencies?.nx ||
workspacePackageJson.dependencies?.nx;
if (declared) {
return declared.replace(/^[\^~>=<]+/, '');
}
}
catch {
// fall through to default
}
return '22.0.0';
}
async function setupAiAgentsGenerator(tree, options, inner = false) {
const normalizedOptions = normalizeOptions(options);
// Use environment variable to force local execution
if (process.env.NX_AI_FILES_USE_LOCAL === 'true' || inner) {
return await setupAiAgentsGeneratorImpl(tree, normalizedOptions);
}
try {
await (0, provenance_1.ensurePackageHasProvenance)('nx', normalizedOptions.packageVersion);
const { tempDir, cleanup } = (0, package_json_1.installPackageToTmp)('nx', normalizedOptions.packageVersion);
let modulePath = (0, path_1.join)(tempDir, 'node_modules', 'nx', 'src/ai/set-up-ai-agents/set-up-ai-agents.js');
const module = await (0, handle_import_1.handleImport)(modulePath);
const setupAiAgentsGeneratorResult = await module.setupAiAgentsGenerator(tree, normalizedOptions, true);
cleanup();
return setupAiAgentsGeneratorResult;
}
catch (error) {
return await setupAiAgentsGeneratorImpl(tree, normalizedOptions);
}
}
function normalizeOptions(options) {
return {
directory: options.directory,
writeNxCloudRules: options.writeNxCloudRules ?? false,
packageVersion: options.packageVersion ?? 'latest',
agents: options.agents ?? [...utils_1.supportedAgents],
};
}
async function setupAiAgentsGeneratorImpl(tree, options) {
const hasAgent = (agent) => options.agents.includes(agent);
const nxVersion = (0, installed_nx_version_1.getInstalledNxVersion)() ?? getDeclaredNxVersionOrDefault();
const agentsMd = (0, constants_1.agentsMdPath)(options.directory);
// write AGENTS.md for most agents
if (hasAgent('cursor') ||
hasAgent('copilot') ||
hasAgent('codex') ||
hasAgent('opencode')) {
writeAgentRules(tree, agentsMd, options.writeNxCloudRules);
}
if (hasAgent('claude')) {
const claudePath = (0, path_1.join)(options.directory, 'CLAUDE.md');
writeAgentRules(tree, claudePath, options.writeNxCloudRules);
// Configure Claude plugin via marketplace (plugin includes MCP server)
const claudeSettingsPath = (0, path_1.join)(options.directory, '.claude', 'settings.json');
if (!tree.exists(claudeSettingsPath)) {
(0, json_1.writeJson)(tree, claudeSettingsPath, {});
}
(0, json_1.updateJson)(tree, claudeSettingsPath, (json) => ({
...json,
extraKnownMarketplaces: {
...json.extraKnownMarketplaces,
'nx-claude-plugins': {
source: {
...json.extraKnownMarketplaces?.['nx-claude-plugins']?.source,
source: 'github',
repo: 'nrwl/nx-ai-agents-config',
},
},
},
enabledPlugins: {
...json.enabledPlugins,
'nx@nx-claude-plugins': true,
},
}));
// Clean up .mcp.json (nx-mcp now handled by plugin)
const mcpJsonPath = (0, constants_1.claudeMcpJsonPath)(options.directory);
if (tree.exists(mcpJsonPath)) {
try {
const mcpJsonContents = (0, json_1.readJson)(tree, mcpJsonPath);
if (mcpJsonContents?.mcpServers?.['nx-mcp']) {
const serverKeys = Object.keys(mcpJsonContents.mcpServers || {});
if (serverKeys.length === 1 && serverKeys[0] === 'nx-mcp') {
// nx-mcp is the only server, delete the file
tree.delete(mcpJsonPath);
}
else {
// Other servers exist, just remove nx-mcp entry
delete mcpJsonContents.mcpServers['nx-mcp'];
(0, json_1.writeJson)(tree, mcpJsonPath, mcpJsonContents);
}
}
}
catch {
// Ignore errors reading .mcp.json
}
}
}
if (hasAgent('opencode')) {
const opencodeMcpJsonPath = (0, constants_1.opencodeMcpPath)(options.directory);
if (!tree.exists(opencodeMcpJsonPath)) {
(0, json_1.writeJson)(tree, opencodeMcpJsonPath, {});
}
(0, json_1.updateJson)(tree, opencodeMcpJsonPath, (json) => opencodeMcpConfigUpdater(json, nxVersion));
}
// Get the ai-config repo path once for all non-Claude agents that need it
const needsAiConfigRepo = hasAgent('codex') ||
hasAgent('opencode') ||
hasAgent('copilot') ||
hasAgent('cursor') ||
hasAgent('gemini');
let aiConfigRepoPath;
if (needsAiConfigRepo) {
try {
aiConfigRepoPath = (0, clone_ai_config_repo_1.getAiConfigRepoPath)();
}
catch {
// Network/clone failure — individual consumers handle fallback
}
}
if (hasAgent('codex')) {
const codexTomlPath = (0, path_1.join)(options.directory, '.codex', 'config.toml');
writeCodexConfig(tree, codexTomlPath, nxVersion, aiConfigRepoPath);
}
if (hasAgent('gemini')) {
const geminiSettingsPath = (0, path_1.join)(options.directory, '.gemini', 'settings.json');
if (!tree.exists(geminiSettingsPath)) {
(0, json_1.writeJson)(tree, geminiSettingsPath, {});
}
(0, json_1.updateJson)(tree, geminiSettingsPath, (json) => mcpConfigUpdater(json, nxVersion));
const contextFileName = (0, json_1.readJson)(tree, geminiSettingsPath).contextFileName;
const geminiMd = (0, constants_1.geminiMdPath)(options.directory);
// Only set contextFileName to AGENTS.md if GEMINI.md doesn't exist already to preserve existing setups
if (!contextFileName && !tree.exists(geminiMd)) {
writeAgentRules(tree, agentsMd, options.writeNxCloudRules);
(0, json_1.updateJson)(tree, geminiSettingsPath, (json) => ({
...json,
contextFileName: 'AGENTS.md',
}));
}
else {
writeAgentRules(tree, contextFileName ?? geminiMd, options.writeNxCloudRules);
}
}
// Copy extensibility artifacts (commands, skills, subagents) for non-Claude agents
if (aiConfigRepoPath) {
const repoPath = aiConfigRepoPath;
// Shared skills directory used by codex, cursor, and gemini
if (hasAgent('codex') || hasAgent('cursor') || hasAgent('gemini')) {
const sharedSkillsSrc = (0, path_1.join)(repoPath, 'generated/.agents');
if ((0, fs_1.existsSync)(sharedSkillsSrc)) {
(0, generate_files_1.generateFiles)(tree, sharedSkillsSrc, (0, path_1.join)(options.directory, '.agents'), {});
}
}
// Agent-specific directories (commands, agents, config)
const agentDirs = [
{ agent: 'opencode', src: 'generated/.opencode', dest: '.opencode' },
{ agent: 'copilot', src: 'generated/.github', dest: '.github' },
{ agent: 'cursor', src: 'generated/.cursor', dest: '.cursor' },
{
agent: 'codex',
src: 'generated/.codex/agents',
dest: '.codex/agents',
},
{ agent: 'gemini', src: 'generated/.gemini', dest: '.gemini' },
];
for (const { agent, src, dest } of agentDirs) {
if (hasAgent(agent)) {
const srcPath = (0, path_1.join)(repoPath, src);
if ((0, fs_1.existsSync)(srcPath)) {
(0, generate_files_1.generateFiles)(tree, srcPath, (0, path_1.join)(options.directory, dest), {});
}
}
}
}
// Clean up legacy .gemini/skills that have been migrated to shared .agents/skills.
// Only delete skills that exist in both locations to preserve user-created skills.
if (hasAgent('gemini')) {
const geminiSkillsDir = (0, path_1.join)(options.directory, '.gemini', 'skills');
const sharedSkillsDir = (0, path_1.join)(options.directory, '.agents', 'skills');
if (tree.exists(geminiSkillsDir) && tree.exists(sharedSkillsDir)) {
const sharedSkills = new Set(tree.children(sharedSkillsDir));
for (const skill of tree.children(geminiSkillsDir)) {
if (sharedSkills.has(skill)) {
tree.delete((0, path_1.join)(geminiSkillsDir, skill));
}
}
}
}
(0, ignore_1.addEntryToGitIgnore)(tree, (0, path_1.join)(options.directory, '.gitignore'), '.nx/polygraph');
(0, ignore_1.addEntryToGitIgnore)(tree, (0, path_1.join)(options.directory, '.gitignore'), '.claude/worktrees');
(0, ignore_1.addEntryToGitIgnore)(tree, (0, path_1.join)(options.directory, '.gitignore'), '.claude/settings.local.json');
await (0, format_changed_files_with_prettier_if_available_1.formatChangedFilesWithPrettierIfAvailable)(tree);
// we use the check variable to determine if we should actually make changes or just report what would be changed
return async (check = false) => {
const messages = [];
const errors = [];
if (hasAgent('copilot')) {
try {
if ((await (0, native_1.isEditorInstalled)(0 /* SupportedEditor.VSCode */)) &&
(await (0, native_1.canInstallNxConsoleForEditor)(0 /* SupportedEditor.VSCode */))) {
if (!check) {
await (0, native_1.installNxConsoleForEditor)(0 /* SupportedEditor.VSCode */);
}
messages.push({
title: `Installed Nx Console for VSCode`,
});
}
}
catch (e) {
errors.push({
title: `Failed to install Nx Console for VSCode. Please install it manually.`,
bodyLines: [e.message],
});
}
try {
if ((await (0, native_1.isEditorInstalled)(1 /* SupportedEditor.VSCodeInsiders */)) &&
(await (0, native_1.canInstallNxConsoleForEditor)(1 /* SupportedEditor.VSCodeInsiders */))) {
if (!check) {
await (0, native_1.installNxConsoleForEditor)(1 /* SupportedEditor.VSCodeInsiders */);
}
messages.push({
title: `Installed Nx Console for VSCode Insiders`,
});
}
}
catch (e) {
errors.push({
title: `Failed to install Nx Console for VSCode Insiders. Please install it manually.`,
bodyLines: [e.message],
});
}
}
if (hasAgent('cursor')) {
try {
if ((await (0, native_1.isEditorInstalled)(2 /* SupportedEditor.Cursor */)) &&
(await (0, native_1.canInstallNxConsoleForEditor)(2 /* SupportedEditor.Cursor */))) {
if (!check) {
await (0, native_1.installNxConsoleForEditor)(2 /* SupportedEditor.Cursor */);
}
messages.push({
title: `Installed Nx Console for Cursor`,
});
}
}
catch (e) {
errors.push({
title: `Failed to install Nx Console for Cursor. Please install it manually.`,
bodyLines: [e.message],
});
}
}
return {
messages,
errors,
};
};
}
function writeAgentRules(tree, path, writeNxCloudRules) {
if (!tree.exists(path)) {
// File doesn't exist - create with h1 header (standalone content)
const expectedRules = (0, constants_1.getAgentRulesWrapped)({
writeNxCloudRules,
useH1: true,
});
tree.write(path, expectedRules);
return;
}
const existing = tree.read(path, 'utf-8');
const regex = constants_1.rulesRegex;
const existingNxConfiguration = existing.match(regex);
if (existingNxConfiguration) {
// Check the rest of the file (outside nx block) for an h1 header
// to ensure only one h1 exists in the document
const contentWithoutNxBlock = existing.replace(regex, '');
const hasExternalH1 = /^# /m.test(contentWithoutNxBlock);
const expectedRules = (0, constants_1.getAgentRulesWrapped)({
writeNxCloudRules,
useH1: !hasExternalH1,
});
const contentOnly = (str) => str
.replace(constants_1.nxRulesMarkerCommentStart, '')
.replace(constants_1.nxRulesMarkerCommentEnd, '')
.replace(constants_1.nxRulesMarkerCommentDescription, '')
.replace(/\s/g, '');
// we don't want to make updates on whitespace-only changes
if (contentOnly(existingNxConfiguration[0]) === contentOnly(expectedRules)) {
return;
}
// otherwise replace the existing configuration
const updatedContent = existing.replace(regex, expectedRules);
tree.write(path, updatedContent);
}
else {
// Appending to existing content - use h2 only if the file already has an h1 header
// This prevents unnecessary changes when users add content without their own h1
const hasExistingH1 = /^# /m.test(existing);
const expectedRules = (0, constants_1.getAgentRulesWrapped)({
writeNxCloudRules,
useH1: !hasExistingH1,
});
tree.write(path, existing + '\n\n' + expectedRules);
}
}
/**
* Write or merge the Codex config.toml.
*
* Reads the generated config.toml from the nx-ai-agents-config repo (which
* contains MCP servers, agent definitions, and feature flags) and deep-merges
* it into the user's existing config.toml using proper TOML parsing.
*
* Merge rules:
* - [mcp_servers."nx-mcp"] — upsert with version-adjusted args, preserving extra user args
* - [features] multi_agent — set to true unless user has explicitly set it to false
* - [agents.*] — upsert each agent definition
* - All other user config is preserved untouched
*
* Falls back to a minimal hardcoded MCP config if the generated file is unavailable.
*/
function writeCodexConfig(tree, codexTomlPath, nxVersion, aiConfigRepoPath) {
let generated = null;
if (aiConfigRepoPath) {
const generatedConfigPath = (0, path_1.join)(aiConfigRepoPath, 'generated', '.codex', 'config.toml');
if ((0, fs_1.existsSync)(generatedConfigPath)) {
const generatedConfig = (0, fs_1.readFileSync)(generatedConfigPath, 'utf-8');
generated = smol_toml_1.default.parse(generatedConfig);
}
}
if (!generated) {
// Fallback: use hardcoded MCP-only config (no agents/features)
const tomlConfig = (0, constants_1.getNxMcpTomlConfig)(nxVersion);
if (!tree.exists(codexTomlPath)) {
tree.write(codexTomlPath, tomlConfig);
}
else {
const existing = tree.read(codexTomlPath, 'utf-8');
if (!existing.includes(constants_1.nxMcpTomlHeader)) {
tree.write(codexTomlPath, existing + '\n' + tomlConfig);
}
}
return;
}
// Parse existing config (or start empty)
let config = {};
if (tree.exists(codexTomlPath)) {
try {
config = smol_toml_1.default.parse(tree.read(codexTomlPath, 'utf-8'));
}
catch {
// If existing file can't be parsed, start fresh
config = {};
}
}
// ── Merge MCP servers ──
const majorVersion = (0, semver_1.major)(nxVersion);
const mcpArgs = majorVersion >= 22 ? ['nx', 'mcp'] : ['nx-mcp'];
// Preserve extra user args from existing config
const existingArgs = config.mcp_servers?.['nx-mcp']?.args ?? [];
const extraArgs = stripKnownMcpBaseArgs(existingArgs);
mcpArgs.push(...extraArgs);
config.mcp_servers ??= {};
config.mcp_servers['nx-mcp'] = {
command: 'npx',
args: mcpArgs,
};
// ── Merge features ──
// Only set multi_agent = true if user hasn't explicitly set it to false
const userSetMultiAgentFalse = config.features?.multi_agent === false;
if (!userSetMultiAgentFalse && generated.features) {
config.features ??= {};
Object.assign(config.features, generated.features);
}
// ── Merge agents ──
if (generated.agents) {
config.agents ??= {};
for (const [name, def] of Object.entries(generated.agents)) {
config.agents[name] = def;
}
}
// ── Serialize and write ──
const tomlString = smol_toml_1.default.stringify(config);
tree.write(codexTomlPath, tomlString);
}
/**
* Strip known MCP base command args (["nx", "mcp"] or ["nx-mcp"]) from an
* args array, returning only the extra user-added args.
*/
function stripKnownMcpBaseArgs(args) {
const knownBasePatterns = [['nx', 'mcp'], ['nx-mcp']];
for (const pattern of knownBasePatterns) {
if (args.length < pattern.length)
continue;
const matches = pattern.every((baseArg, i) => {
if (baseArg === 'nx-mcp') {
return args[i] === 'nx-mcp' || args[i].startsWith('nx-mcp@');
}
return args[i] === baseArg;
});
if (matches) {
return args.slice(pattern.length);
}
}
return [];
}
/**
* Extract user-added extra args/flags from an existing MCP config args array
* by stripping the known base command prefix.
*
* Known base patterns (matched in order, first wins):
* ['nx', 'mcp'] or ['nx-mcp'] (possibly with @version suffix like nx-mcp@latest)
* For opencode the caller prepends 'npx' to these patterns.
*/
function getExtraMcpArgs(existingArgs, knownBasePatterns) {
if (!Array.isArray(existingArgs) || existingArgs.length === 0)
return [];
for (const pattern of knownBasePatterns) {
if (existingArgs.length < pattern.length)
continue;
const matches = pattern.every((baseArg, i) => {
if (baseArg === 'nx-mcp') {
// Also match versioned variants like nx-mcp@latest
return (existingArgs[i] === 'nx-mcp' || existingArgs[i].startsWith('nx-mcp@'));
}
return existingArgs[i] === baseArg;
});
if (matches) {
return existingArgs.slice(pattern.length);
}
}
return [];
}
function mcpConfigUpdater(existing, nxVersion) {
const majorVersion = (0, semver_1.major)(nxVersion);
const mcpArgs = majorVersion >= 22 ? ['nx', 'mcp'] : ['nx-mcp'];
// Preserve any extra args (e.g. --experimental-polygraph, --transport http) from existing config
const extraArgs = getExtraMcpArgs(existing.mcpServers?.['nx-mcp']?.args, [
['nx', 'mcp'],
['nx-mcp'],
]);
mcpArgs.push(...extraArgs);
if (existing.mcpServers) {
existing.mcpServers['nx-mcp'] = {
type: 'stdio',
command: 'npx',
args: mcpArgs,
};
}
else {
existing.mcpServers = {
'nx-mcp': {
type: 'stdio',
command: 'npx',
args: mcpArgs,
},
};
}
return existing;
}
function opencodeMcpConfigUpdater(existing, nxVersion) {
const majorVersion = (0, semver_1.major)(nxVersion);
const mcpCommand = majorVersion >= 22 ? ['npx', 'nx', 'mcp'] : ['npx', 'nx-mcp'];
// Preserve any extra args (e.g. --experimental-polygraph, --transport http) from existing config
const extraArgs = getExtraMcpArgs(existing.mcp?.['nx-mcp']?.command, [
['npx', 'nx', 'mcp'],
['npx', 'nx-mcp'],
]);
mcpCommand.push(...extraArgs);
if (existing.mcp) {
existing.mcp['nx-mcp'] = {
type: 'local',
command: mcpCommand,
enabled: true,
};
}
else {
existing.mcp = {
'nx-mcp': {
type: 'local',
command: mcpCommand,
enabled: true,
},
};
}
return existing;
}
exports.default = setupAiAgentsGenerator;

20
node_modules/nx/dist/src/ai/utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
export declare const supportedAgents: readonly ["claude", "codex", "copilot", "cursor", "gemini", "opencode"];
export type Agent = (typeof supportedAgents)[number];
export declare const agentDisplayMap: Record<Agent, string>;
export type AgentConfiguration = {
name: Agent;
displayName: string;
rules: boolean;
mcp: boolean;
rulesPath: string;
mcpPath: string | null;
outdated: boolean;
disabled?: boolean;
};
export declare function getAgentConfigurations(agentsToConsider: Agent[], workspaceRoot: string): Promise<{
nonConfiguredAgents: AgentConfiguration[];
partiallyConfiguredAgents: AgentConfiguration[];
fullyConfiguredAgents: AgentConfiguration[];
disabledAgents: AgentConfiguration[];
}>;
export declare function configureAgents(agents: Agent[], workspaceRoot: string, useLatest?: boolean): Promise<void>;

217
node_modules/nx/dist/src/ai/utils.js generated vendored Normal file
View File

@@ -0,0 +1,217 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.agentDisplayMap = exports.supportedAgents = void 0;
exports.getAgentConfigurations = getAgentConfigurations;
exports.configureAgents = configureAgents;
const tslib_1 = require("tslib");
const fs_1 = require("fs");
const path_1 = require("path");
const configuration_1 = require("../config/configuration");
const tree_1 = require("../generators/tree");
const native_1 = require("../native");
const fileutils_1 = require("../utils/fileutils");
const nx_cloud_utils_1 = require("../utils/nx-cloud-utils");
const output_1 = require("../utils/output");
const constants_1 = require("./constants");
const set_up_ai_agents_1 = tslib_1.__importDefault(require("./set-up-ai-agents/set-up-ai-agents"));
// when adding new agents, be sure to also update the list in
// packages/create-nx-workspace/src/create-workspace-options.ts
exports.supportedAgents = [
'claude',
'codex',
'copilot',
'cursor',
'gemini',
'opencode',
];
exports.agentDisplayMap = {
claude: 'Claude Code',
gemini: 'Gemini',
codex: 'OpenAI Codex',
copilot: 'GitHub Copilot for VSCode',
cursor: 'Cursor',
opencode: 'OpenCode',
};
async function getAgentConfigurations(agentsToConsider, workspaceRoot) {
const nonConfiguredAgents = [];
const partiallyConfiguredAgents = [];
const fullyConfiguredAgents = [];
const disabledAgents = [];
for (const agent of agentsToConsider) {
const configuration = await getAgentConfiguration(agent, workspaceRoot);
if (configuration.disabled) {
disabledAgents.push(configuration);
continue;
}
if (configuration.mcp && configuration.rules) {
fullyConfiguredAgents.push(configuration);
}
else if (!configuration.mcp && !configuration.rules) {
nonConfiguredAgents.push(configuration);
}
else {
partiallyConfiguredAgents.push(configuration);
}
}
return {
nonConfiguredAgents,
partiallyConfiguredAgents,
fullyConfiguredAgents,
disabledAgents,
};
}
async function getAgentConfiguration(agent, workspaceRoot) {
let agentConfiguration;
switch (agent) {
case 'claude': {
// Claude uses a plugin from marketplace which includes the MCP server
const claudeSettingsPath = (0, path_1.resolve)(workspaceRoot, '.claude', 'settings.json');
let pluginConfigured;
try {
const settingsContents = (0, fileutils_1.readJsonFile)(claudeSettingsPath);
pluginConfigured =
!!settingsContents?.['enabledPlugins']?.['nx@nx-claude-plugins'];
}
catch {
pluginConfigured = false;
}
const rulesPath = (0, constants_1.claudeMdPath)(workspaceRoot);
const rulesExists = (0, fs_1.existsSync)(rulesPath);
agentConfiguration = {
rules: rulesExists,
mcp: pluginConfigured,
rulesPath: rulesPath,
mcpPath: claudeSettingsPath,
};
break;
}
case 'gemini': {
const geminiRulePath = (0, constants_1.geminiMdPath)(workspaceRoot);
const geminiMdExists = (0, fs_1.existsSync)(geminiRulePath);
const settingsPath = (0, constants_1.geminiSettingsPath)(workspaceRoot);
let mcpConfigured;
const geminiSettings = (0, constants_1.parseGeminiSettings)(workspaceRoot);
const customContextFilePath = typeof geminiSettings?.contextFileName === 'string'
? geminiSettings.contextFileName
: undefined;
const customContextFilePathExists = customContextFilePath
? (0, fs_1.existsSync)((0, path_1.resolve)(workspaceRoot, customContextFilePath))
: false;
mcpConfigured = geminiSettings?.['mcpServers']?.['nx-mcp'];
agentConfiguration = {
rules: (!customContextFilePath && geminiMdExists) ||
(customContextFilePath && customContextFilePathExists),
mcp: mcpConfigured,
rulesPath: customContextFilePath ?? geminiRulePath,
mcpPath: settingsPath,
};
break;
}
case 'copilot': {
const rulesPath = (0, constants_1.agentsMdPath)(workspaceRoot);
const hasInstalledVSCode = await (0, native_1.isEditorInstalled)(0 /* SupportedEditor.VSCode */);
const hasInstalledVSCodeInsiders = await (0, native_1.isEditorInstalled)(1 /* SupportedEditor.VSCodeInsiders */);
const hasInstalledNxConsoleForVSCode = hasInstalledVSCode &&
!(await (0, native_1.canInstallNxConsoleForEditor)(0 /* SupportedEditor.VSCode */));
const hasInstalledNxConsoleForVSCodeInsiders = hasInstalledVSCodeInsiders &&
!(await (0, native_1.canInstallNxConsoleForEditor)(1 /* SupportedEditor.VSCodeInsiders */));
const agentsMdExists = (0, fs_1.existsSync)(rulesPath);
agentConfiguration = {
mcp: hasInstalledNxConsoleForVSCode ||
hasInstalledNxConsoleForVSCodeInsiders,
rules: agentsMdExists,
rulesPath,
mcpPath: null,
disabled: !hasInstalledVSCode && !hasInstalledVSCodeInsiders,
};
break;
}
case 'cursor': {
const rulesPath = (0, constants_1.agentsMdPath)(workspaceRoot);
const hasInstalledCursor = await (0, native_1.isEditorInstalled)(2 /* SupportedEditor.Cursor */);
const hasInstalledNxConsole = !(await (0, native_1.canInstallNxConsoleForEditor)(2 /* SupportedEditor.Cursor */));
const agentsMdExists = (0, fs_1.existsSync)(rulesPath);
agentConfiguration = {
mcp: hasInstalledCursor ? hasInstalledNxConsole : false,
rules: agentsMdExists,
rulesPath,
mcpPath: null,
disabled: !hasInstalledCursor,
};
break;
}
case 'codex': {
const rulesPath = (0, constants_1.agentsMdPath)(workspaceRoot);
const agentsMdExists = (0, fs_1.existsSync)(rulesPath);
const mcpPath = (0, constants_1.codexConfigTomlPath)(workspaceRoot);
let mcpConfigured;
if ((0, fs_1.existsSync)(mcpPath)) {
const tomlContents = (0, fs_1.readFileSync)(mcpPath, 'utf-8');
mcpConfigured = tomlContents.includes(constants_1.nxMcpTomlHeader);
}
else {
mcpConfigured = false;
}
agentConfiguration = {
mcp: mcpConfigured,
rules: agentsMdExists,
rulesPath,
mcpPath,
};
break;
}
case 'opencode': {
const rulesPath = (0, constants_1.agentsMdPath)(workspaceRoot);
const agentsMdExists = (0, fs_1.existsSync)(rulesPath);
const mcpPath = (0, constants_1.opencodeMcpPath)(workspaceRoot);
let mcpConfigured;
try {
const mcpContents = (0, fileutils_1.readJsonFile)(mcpPath);
// OpenCode uses 'mcp' property, not 'mcpServers'
mcpConfigured = !!mcpContents?.['mcp']?.['nx-mcp'];
}
catch {
mcpConfigured = false;
}
agentConfiguration = {
mcp: mcpConfigured,
rules: agentsMdExists,
rulesPath,
mcpPath,
};
break;
}
}
return {
...agentConfiguration,
outdated: agentConfiguration.mcp &&
agentConfiguration.rules &&
(await agentWouldChangeWithGenerator(agent, workspaceRoot)),
name: agent,
displayName: exports.agentDisplayMap[agent],
};
}
async function agentWouldChangeWithGenerator(agent, workspaceRoot) {
const tree = new tree_1.FsTree(workspaceRoot, false);
const callback = await (0, set_up_ai_agents_1.default)(tree, {
directory: '.',
agents: [agent],
writeNxCloudRules: (0, nx_cloud_utils_1.isNxCloudUsed)((0, configuration_1.readNxJson)()),
}, true);
const modificationResults = await callback(true);
return (tree.listChanges().length > 0 || modificationResults.messages.length > 0);
}
async function configureAgents(agents, workspaceRoot, useLatest) {
const writeNxCloudRules = (0, nx_cloud_utils_1.isNxCloudUsed)((0, configuration_1.readNxJson)());
const tree = new tree_1.FsTree(workspaceRoot, false);
const callback = await (0, set_up_ai_agents_1.default)(tree, {
directory: '.',
agents,
writeNxCloudRules,
}, !useLatest);
// changes that are out of scope for the generator itself because they do more than modify the tree
(0, tree_1.flushChanges)(workspaceRoot, tree.listChanges());
const modificationResults = await callback();
modificationResults.messages.forEach((message) => output_1.output.log(message));
modificationResults.errors.forEach((error) => output_1.output.error(error));
}

10
node_modules/nx/dist/src/analytics/analytics.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
import type { EventDimensions } from '../native';
export declare const customDimensions: EventDimensions;
export type EventParameters = Partial<Record<EventDimensions[keyof EventDimensions], string | number | boolean>>;
export declare function startAnalytics(): Promise<void>;
export declare function reportNxAddCommand(packageName: string, version: string): void;
export declare function reportNxGenerateCommand(generator: string): void;
export declare function reportCommandRunEvent(command: string, parameters?: Record<string, any>, args?: Record<string, any>): void;
export declare function reportEvent(name: string, eventParameters?: EventParameters): void;
export declare function argsToQueryString(args: Record<string, any>): string;
export declare function flushAnalytics(): void;

236
node_modules/nx/dist/src/analytics/analytics.js generated vendored Normal file
View File

@@ -0,0 +1,236 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.customDimensions = void 0;
exports.startAnalytics = startAnalytics;
exports.reportNxAddCommand = reportNxAddCommand;
exports.reportNxGenerateCommand = reportNxGenerateCommand;
exports.reportCommandRunEvent = reportCommandRunEvent;
exports.reportEvent = reportEvent;
exports.argsToQueryString = argsToQueryString;
exports.flushAnalytics = flushAnalytics;
const tslib_1 = require("tslib");
const nx_json_1 = require("../config/nx-json");
const workspace_root_1 = require("../utils/workspace-root");
const versions_1 = require("../utils/versions");
const native_1 = require("../native");
const package_manager_1 = require("../utils/package-manager");
const semver_1 = require("semver");
const os = tslib_1.__importStar(require("os"));
const crypto_1 = require("crypto");
const machine_id_cache_1 = require("../utils/machine-id-cache");
const is_ci_1 = require("../utils/is-ci");
const analytics_prompt_1 = require("../utils/analytics-prompt");
const db_connection_1 = require("../utils/db-connection");
// Conditionally import telemetry functions only on non-WASM platforms
let initializeTelemetry;
let initializeTelemetryWithSessionId;
let flushTelemetry;
let trackEventNative;
let trackPageViewNative;
let getEventDimensions;
if (!native_1.IS_WASM) {
const nativeModule = require('../native');
initializeTelemetry = nativeModule.initializeTelemetry;
initializeTelemetryWithSessionId =
nativeModule.initializeTelemetryWithSessionId;
flushTelemetry = nativeModule.flushTelemetry;
trackEventNative = nativeModule.trackEvent;
trackPageViewNative = nativeModule.trackPageView;
getEventDimensions = nativeModule.getEventDimensions;
}
exports.customDimensions = native_1.IS_WASM
? null
: (getEventDimensions?.() ?? null);
let _telemetryInitialized = false;
async function startAnalytics() {
// Analytics not supported on WASM
if (native_1.IS_WASM) {
return;
}
if (!isAnalyticsEnabled()) {
return;
}
const nxJson = (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot);
const workspaceId = (0, analytics_prompt_1.generateWorkspaceId)();
if (!workspaceId) {
// Not a git repo — no telemetry
return;
}
const isNxCloud = !!(nxJson?.nxCloudId ?? nxJson?.nxCloudAccessToken);
const userId = await getTelemetryUserId(workspaceId);
const packageManagerInfo = getPackageManagerInfo();
const nodeVersion = (0, semver_1.parse)(process.version);
const nodeVersionString = nodeVersion
? `${nodeVersion.major}.${nodeVersion.minor}.${nodeVersion.patch}`
: 'unknown';
const commonArgs = [
workspaceId,
userId,
versions_1.nxVersion,
packageManagerInfo.name,
packageManagerInfo.version,
nodeVersionString,
os.arch(),
os.platform(),
os.release(),
!!(0, is_ci_1.isCI)(),
isNxCloud,
];
try {
const sessionId = process.env.NX_ANALYTICS_SESSION_ID;
if (sessionId) {
// Plugin worker path — reuse session ID from parent, no DB needed
initializeTelemetryWithSessionId(sessionId, ...commonArgs);
}
else {
// CLI/daemon path — get session from DB, set env var for children
const dbConnection = (0, db_connection_1.getDbConnection)();
const newSessionId = initializeTelemetry(dbConnection, ...commonArgs);
process.env.NX_ANALYTICS_SESSION_ID = newSessionId;
}
_telemetryInitialized = true;
// Flush analytics automatically on process exit so every code path
// is covered without needing explicit exitAndFlushAnalytics() calls.
process.on('exit', () => {
flushAnalytics();
});
}
catch (error) {
// If telemetry service fails to initialize, continue without it
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.log(`Failed to initialize telemetry: ${error.message}`);
}
}
}
function reportNxAddCommand(packageName, version) {
reportCommandRunEvent('add', {
[exports.customDimensions.packageName]: packageName,
[exports.customDimensions.packageVersion]: version,
});
}
function reportNxGenerateCommand(generator) {
reportCommandRunEvent('generate', {
[exports.customDimensions.generatorName]: generator,
});
}
function reportCommandRunEvent(command, parameters, args) {
command = command === 'g' ? 'generate' : command;
let pageLocation = command;
if (args) {
const qs = argsToQueryString(args);
if (qs) {
pageLocation = `${command}?${qs}`;
}
}
trackPageView(command, pageLocation, parameters);
}
function reportEvent(name, eventParameters) {
trackEvent(name, eventParameters);
}
const SKIP_ARGS_KEYS = new Set([
'$0',
'_',
'__overrides_unparsed__',
'__overrides__',
]);
// String args with fixed enum values that are safe to include in analytics.
// All boolean and number args are included automatically.
const ALLOWED_STRING_ARGS = new Set([
'outputStyle',
'type',
'view',
'access',
'preset',
'interactive',
'printConfig',
'resolveVersionPlans',
]);
function argsToQueryString(args) {
const params = new URLSearchParams();
for (const [key, value] of Object.entries(args)) {
if (SKIP_ARGS_KEYS.has(key))
continue;
if (value === undefined || value === null)
continue;
if (typeof value === 'boolean' || typeof value === 'number') {
params.append(key, String(value));
}
else if (typeof value === 'string' && ALLOWED_STRING_ARGS.has(key)) {
params.append(key, value);
}
// All other types (strings, arrays, objects) are dropped
}
return params.toString();
}
function trackEvent(eventName, parameters) {
if (_telemetryInitialized) {
// Convert parameters to string map for Rust
const stringParams = {};
if (parameters) {
for (const [key, value] of Object.entries(parameters)) {
if (value !== undefined && value !== null) {
stringParams[key] = String(value);
}
}
}
// Fire and forget - synchronous call
try {
trackEventNative(eventName, stringParams);
}
catch {
// Silently ignore errors
}
}
}
function trackPageView(pageTitle, pageLocation, parameters) {
if (_telemetryInitialized) {
// Convert parameters to string map for Rust
const stringParams = {};
if (parameters) {
for (const [key, value] of Object.entries(parameters)) {
if (value !== undefined && value !== null) {
stringParams[key] = String(value);
}
}
}
// Fire and forget - synchronous call
try {
trackPageViewNative(pageTitle, pageLocation, stringParams);
}
catch {
// Silently ignore errors
}
}
}
function flushAnalytics() {
if (_telemetryInitialized) {
try {
flushTelemetry();
}
catch (error) {
// Failure to report analytics shouldn't crash the CLI
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.log(`Failed to flush telemetry: ${error.message}`);
}
}
}
}
function getPackageManagerInfo() {
const pm = (0, package_manager_1.detectPackageManager)();
return {
name: pm,
version: (0, package_manager_1.getPackageManagerVersion)(pm),
};
}
function isAnalyticsEnabled() {
const nxJson = (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot);
return nxJson?.analytics === true;
}
// Mix workspace id in: shared Docker images (Gitpod, Cypress, etc.) bake
// in /etc/machine-id, so machine-id alone collapses many users into one.
async function getTelemetryUserId(workspaceId) {
const machineId = await (0, machine_id_cache_1.getCurrentMachineId)();
return (0, crypto_1.createHash)('sha256')
.update(`${machineId}|${workspaceId}`)
.digest('hex');
}

1
node_modules/nx/dist/src/analytics/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export { customDimensions, EventParameters, startAnalytics, reportNxAddCommand, reportNxGenerateCommand, reportCommandRunEvent, reportEvent, flushAnalytics, } from './analytics';

11
node_modules/nx/dist/src/analytics/index.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.flushAnalytics = exports.reportEvent = exports.reportCommandRunEvent = exports.reportNxGenerateCommand = exports.reportNxAddCommand = exports.startAnalytics = exports.customDimensions = void 0;
var analytics_1 = require("./analytics");
Object.defineProperty(exports, "customDimensions", { enumerable: true, get: function () { return analytics_1.customDimensions; } });
Object.defineProperty(exports, "startAnalytics", { enumerable: true, get: function () { return analytics_1.startAnalytics; } });
Object.defineProperty(exports, "reportNxAddCommand", { enumerable: true, get: function () { return analytics_1.reportNxAddCommand; } });
Object.defineProperty(exports, "reportNxGenerateCommand", { enumerable: true, get: function () { return analytics_1.reportNxGenerateCommand; } });
Object.defineProperty(exports, "reportCommandRunEvent", { enumerable: true, get: function () { return analytics_1.reportCommandRunEvent; } });
Object.defineProperty(exports, "reportEvent", { enumerable: true, get: function () { return analytics_1.reportEvent; } });
Object.defineProperty(exports, "flushAnalytics", { enumerable: true, get: function () { return analytics_1.flushAnalytics; } });

3
node_modules/nx/dist/src/command-line/add/add.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
import type { AddOptions } from './command-object';
export declare function addHandler(options: AddOptions): Promise<number>;
export declare const coreNxPluginVersions: Map<string, string>;

126
node_modules/nx/dist/src/command-line/add/add.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.coreNxPluginVersions = void 0;
exports.addHandler = addHandler;
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const nx_json_1 = require("../../config/nx-json");
const child_process_2 = require("../../utils/child-process");
const fileutils_1 = require("../../utils/fileutils");
const logger_1 = require("../../utils/logger");
const output_1 = require("../../utils/output");
const package_manager_1 = require("../../utils/package-manager");
const handle_errors_1 = require("../../utils/handle-errors");
const versions_1 = require("../../utils/versions");
const workspace_root_1 = require("../../utils/workspace-root");
const add_nx_scripts_1 = require("../init/implementation/dot-nx/add-nx-scripts");
const semver_1 = require("semver");
const configure_plugins_1 = require("../init/configure-plugins");
const spinner_1 = require("../../utils/spinner");
const analytics_1 = require("../../analytics");
function addHandler(options) {
return (0, handle_errors_1.handleErrors)(options.verbose, async () => {
output_1.output.addNewline();
const [pkgName, version] = parsePackageSpecifier(options.packageSpecifier);
(0, analytics_1.reportNxAddCommand)(pkgName, version);
const nxJson = (0, nx_json_1.readNxJson)();
await installPackage(pkgName, version, nxJson);
await initializePlugin(pkgName, options, nxJson);
output_1.output.success({
title: `Package ${pkgName} added successfully.`,
});
});
}
async function installPackage(pkgName, version, nxJson) {
const spinner = spinner_1.globalSpinner.start(`Installing ${pkgName}@${version}...`);
if ((0, fs_1.existsSync)('package.json')) {
const pm = (0, package_manager_1.detectPackageManager)();
const pmv = (0, package_manager_1.getPackageManagerVersion)(pm);
const pmc = (0, package_manager_1.getPackageManagerCommand)(pm);
// if we explicitly specify latest in yarn berry, it won't resolve the version
const command = pm === 'yarn' && (0, semver_1.gte)(pmv, '2.0.0') && version === 'latest'
? `${pmc.addDev} ${pkgName}`
: `${pmc.addDev} ${pkgName}@${version}`;
await new Promise((resolve) => (0, child_process_1.exec)(command, {
windowsHide: true,
}, (error, stdout, stderr) => {
if (error) {
spinner.fail();
output_1.output.addNewline();
const errorOutput = [stdout.trim(), stderr.trim()]
.filter(Boolean)
.join('\n');
logger_1.logger.error(errorOutput);
output_1.output.error({
title: `Failed to install ${pkgName}. Please check the error above for more details.`,
});
process.exit(1);
}
return resolve();
}));
}
else {
nxJson.installation.plugins ??= {};
nxJson.installation.plugins[pkgName] = (0, add_nx_scripts_1.normalizeVersionForNxJson)(pkgName, version);
(0, fileutils_1.writeJsonFile)('nx.json', nxJson);
try {
await (0, child_process_2.runNxAsync)('--help', { silent: true });
}
catch (e) {
// revert adding the plugin to nx.json
nxJson.installation.plugins[pkgName] = undefined;
(0, fileutils_1.writeJsonFile)('nx.json', nxJson);
spinner.fail();
output_1.output.addNewline();
logger_1.logger.error(e.message);
output_1.output.error({
title: `Failed to install ${pkgName}. Please check the error above for more details.`,
});
process.exit(1);
}
}
spinner.succeed();
}
async function initializePlugin(pkgName, options, nxJson) {
let updatePackageScripts = false;
if (exports.coreNxPluginVersions.has(pkgName) &&
(options.updatePackageScripts ||
(options.updatePackageScripts === undefined &&
nxJson.useInferencePlugins !== false &&
process.env.NX_ADD_PLUGINS !== 'false'))) {
updatePackageScripts = true;
}
const spinner = spinner_1.globalSpinner.start(`Initializing ${pkgName}...`);
try {
await (0, configure_plugins_1.runPluginInitGenerator)(pkgName, workspace_root_1.workspaceRoot, updatePackageScripts, options.verbose);
}
catch (e) {
spinner.fail();
output_1.output.addNewline();
output_1.output.error({
title: `Failed to initialize ${pkgName}`,
bodyLines: (0, configure_plugins_1.getFailedToInstallPluginErrorMessages)(e),
});
process.exit(1);
}
spinner.succeed();
}
function parsePackageSpecifier(packageSpecifier) {
const i = packageSpecifier.lastIndexOf('@');
if (i <= 0) {
if (exports.coreNxPluginVersions.has(packageSpecifier)) {
return [packageSpecifier, exports.coreNxPluginVersions.get(packageSpecifier)];
}
return [packageSpecifier, 'latest'];
}
const pkgName = packageSpecifier.substring(0, i);
const version = packageSpecifier.substring(i + 1);
return [pkgName, version];
}
exports.coreNxPluginVersions = require(require.resolve('nx/package.json'))['nx-migrations'].packageGroup.reduce((map, entry) => {
const packageName = typeof entry === 'string' ? entry : entry.package;
const version = typeof entry === 'string' ? versions_1.nxVersion : entry.version;
return map.set(packageName, version);
},
// Package Name -> Desired Version
new Map());

View File

@@ -0,0 +1,8 @@
import { CommandModule } from 'yargs';
export interface AddOptions {
packageSpecifier: string;
updatePackageScripts?: boolean;
verbose?: boolean;
__overrides_unparsed__: string[];
}
export declare const yargsAddCommand: CommandModule<{}, AddOptions>;

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsAddCommand = void 0;
const handle_import_1 = require("../../utils/handle-import");
const shared_options_1 = require("../yargs-utils/shared-options");
exports.yargsAddCommand = {
command: 'add <packageSpecifier>',
describe: 'Install a plugin and initialize it.',
builder: (yargs) => (0, shared_options_1.withVerbose)(yargs)
.parserConfiguration({
'strip-dashed': true,
'unknown-options-as-args': true,
})
.positional('packageSpecifier', {
type: 'string',
description: 'The package name and optional version (e.g. `@nx/react` or `@nx/react@latest`) to install and initialize. If the version is not specified it will install the same version as the `nx` package for Nx core plugins or the latest version for other packages.',
})
.option('updatePackageScripts', {
type: 'boolean',
description: 'Update `package.json` scripts with inferred targets. Defaults to `true` when the package is a core Nx plugin.',
})
.example('$0 add @nx/react', 'Install the latest version of the `@nx/react` package and run its `@nx/react:init` generator')
.example('$0 add non-core-nx-plugin', 'Install the latest version of the `non-core-nx-plugin` package and run its `non-core-nx-plugin:init` generator if available')
.example('$0 add @nx/react@17.0.0', 'Install version `17.0.0` of the `@nx/react` package and run its `@nx/react:init` generator'),
handler: async (args) => {
process.exit(await (0, handle_import_1.handleImport)('./add.js', __dirname).then((m) => m.addHandler((0, shared_options_1.withOverrides)(args))));
},
};

View File

@@ -0,0 +1,10 @@
import type { NxArgs } from '../../utils/command-line-utils';
import { ProjectGraph, ProjectGraphProjectNode } from '../../config/project-graph';
import { TargetDependencyConfig } from '../../config/workspace-json-project-json';
export declare function affected(command: 'graph' | 'print-affected' | 'affected', args: {
[k: string]: any;
}, extraTargetDependencies?: Record<string, (TargetDependencyConfig | string)[]>, extraOptions?: {
excludeTaskDependencies: boolean;
loadDotEnvFiles: boolean;
}): Promise<void>;
export declare function getAffectedGraphNodes(nxArgs: NxArgs, projectGraph: ProjectGraph): Promise<ProjectGraphProjectNode[]>;

View File

@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.affected = affected;
exports.getAffectedGraphNodes = getAffectedGraphNodes;
const file_utils_1 = require("../../project-graph/file-utils");
const run_command_1 = require("../../tasks-runner/run-command");
const output_1 = require("../../utils/output");
const connect_to_nx_cloud_1 = require("../nx-cloud/connect/connect-to-nx-cloud");
const command_line_utils_1 = require("../../utils/command-line-utils");
const perf_hooks_1 = require("perf_hooks");
const project_graph_1 = require("../../project-graph/project-graph");
const project_graph_utils_1 = require("../../utils/project-graph-utils");
const affected_project_graph_1 = require("../../project-graph/affected/affected-project-graph");
const configuration_1 = require("../../config/configuration");
const find_matching_projects_1 = require("../../utils/find-matching-projects");
const graph_1 = require("../graph/graph");
async function affected(command, args, extraTargetDependencies = {}, extraOptions = {
excludeTaskDependencies: args.excludeTaskDependencies,
loadDotEnvFiles: process.env.NX_LOAD_DOT_ENV_FILES !== 'false',
}) {
perf_hooks_1.performance.mark('code-loading:end');
perf_hooks_1.performance.measure('code-loading', 'init-local', 'code-loading:end');
const nxJson = (0, configuration_1.readNxJson)();
const { nxArgs, overrides } = (0, command_line_utils_1.splitArgsIntoNxArgsAndOverrides)(args, 'affected', {
printWarnings: command !== 'print-affected' && !args.plain && args.graph !== 'stdout',
}, nxJson);
await (0, connect_to_nx_cloud_1.connectToNxCloudIfExplicitlyAsked)(nxArgs);
const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({
exitOnError: true,
});
const projects = await getAffectedGraphNodes(nxArgs, projectGraph);
try {
switch (command) {
case 'affected': {
const projectsWithTarget = allProjectsWithTarget(projects, nxArgs);
if (nxArgs.graph) {
const projectNames = projectsWithTarget.map((t) => t.name);
const file = (0, command_line_utils_1.readGraphFileFromGraphArg)(nxArgs);
return await (0, graph_1.generateGraph)({
watch: true,
open: true,
view: 'tasks',
targets: nxArgs.targets,
all: nxArgs.all &&
(!nxArgs.projects || nxArgs.projects.length === 0),
projects: projectNames,
file,
}, projectNames);
}
else {
const status = await (0, run_command_1.runCommand)(projectsWithTarget, projectGraph, { nxJson }, nxArgs, overrides, null, extraTargetDependencies, extraOptions);
process.exit(status);
}
break;
}
}
await output_1.output.drain();
}
catch (e) {
printError(e, args.verbose);
process.exit(1);
}
}
async function getAffectedGraphNodes(nxArgs, projectGraph) {
let affectedGraph = nxArgs.all
? projectGraph
: await (0, affected_project_graph_1.filterAffected)(projectGraph, (0, file_utils_1.calculateFileChanges)((0, command_line_utils_1.parseFiles)(nxArgs).files, nxArgs));
if (nxArgs.exclude) {
const excludedProjects = new Set((0, find_matching_projects_1.findMatchingProjects)(nxArgs.exclude, affectedGraph.nodes));
return Object.entries(affectedGraph.nodes)
.filter(([projectName]) => !excludedProjects.has(projectName))
.map(([, project]) => project);
}
return Object.values(affectedGraph.nodes);
}
function allProjectsWithTarget(projects, nxArgs) {
return projects.filter((p) => nxArgs.targets.find((target) => (0, project_graph_utils_1.projectHasTarget)(p, target)));
}
function printError(e, verbose) {
const bodyLines = [e.message];
if (verbose && e.stack) {
bodyLines.push('');
bodyLines.push(e.stack);
}
output_1.output.error({
title: 'There was a critical error when running your command',
bodyLines,
});
}

View File

@@ -0,0 +1,6 @@
import { CommandModule } from 'yargs';
export declare const yargsAffectedCommand: CommandModule;
export declare const yargsAffectedTestCommand: CommandModule;
export declare const yargsAffectedBuildCommand: CommandModule;
export declare const yargsAffectedLintCommand: CommandModule;
export declare const yargsAffectedE2ECommand: CommandModule;

View File

@@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsAffectedE2ECommand = exports.yargsAffectedLintCommand = exports.yargsAffectedBuildCommand = exports.yargsAffectedTestCommand = exports.yargsAffectedCommand = void 0;
const handle_errors_1 = require("../../utils/handle-errors");
const handle_import_1 = require("../../utils/handle-import");
const documentation_1 = require("../yargs-utils/documentation");
const shared_options_1 = require("../yargs-utils/shared-options");
exports.yargsAffectedCommand = {
command: 'affected',
describe: 'Run target for affected projects. Affected projects are projects that have been changed and projects that depend on the changed projects. See https://nx.dev/ci/features/affected for more details.',
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withAffectedOptions)((0, shared_options_1.withTuiOptions)((0, shared_options_1.withRunOptions)((0, shared_options_1.withOutputStyleOption)((0, shared_options_1.withTargetAndConfigurationOption)((0, shared_options_1.withBatch)(yargs))))))
.option('all', {
type: 'boolean',
deprecated: 'Use `nx run-many` instead',
})
.middleware((args) => {
if (args.all !== undefined) {
throw new Error("The '--all' option has been removed for `nx affected`. Use 'nx run-many' instead.");
}
}), 'affected'),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(args.verbose ?? process.env.NX_VERBOSE_LOGGING === 'true', async () => {
return (await (0, handle_import_1.handleImport)('./affected.js', __dirname)).affected('affected', (0, shared_options_1.withOverrides)(args));
});
process.exit(exitCode);
},
};
exports.yargsAffectedTestCommand = {
command: 'affected:test',
describe: false,
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withAffectedOptions)((0, shared_options_1.withTuiOptions)((0, shared_options_1.withRunOptions)((0, shared_options_1.withOutputStyleOption)((0, shared_options_1.withConfiguration)(yargs))))), 'affected'),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(args.verbose ?? process.env.NX_VERBOSE_LOGGING === 'true', async () => {
return (await (0, handle_import_1.handleImport)('./affected.js', __dirname)).affected('affected', {
...(0, shared_options_1.withOverrides)(args),
target: 'test',
});
});
process.exit(exitCode);
},
};
exports.yargsAffectedBuildCommand = {
command: 'affected:build',
describe: false,
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withAffectedOptions)((0, shared_options_1.withTuiOptions)((0, shared_options_1.withRunOptions)((0, shared_options_1.withOutputStyleOption)((0, shared_options_1.withConfiguration)(yargs))))), 'affected'),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(args.verbose ?? process.env.NX_VERBOSE_LOGGING === 'true', async () => {
return (await (0, handle_import_1.handleImport)('./affected.js', __dirname)).affected('affected', {
...(0, shared_options_1.withOverrides)(args),
target: 'build',
});
});
process.exit(exitCode);
},
};
exports.yargsAffectedLintCommand = {
command: 'affected:lint',
describe: false,
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withAffectedOptions)((0, shared_options_1.withTuiOptions)((0, shared_options_1.withRunOptions)((0, shared_options_1.withOutputStyleOption)((0, shared_options_1.withConfiguration)(yargs))))), 'affected'),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(args.verbose ?? process.env.NX_VERBOSE_LOGGING === 'true', async () => {
return (await (0, handle_import_1.handleImport)('./affected.js', __dirname)).affected('affected', {
...(0, shared_options_1.withOverrides)(args),
target: 'lint',
});
});
process.exit(exitCode);
},
};
exports.yargsAffectedE2ECommand = {
command: 'affected:e2e',
describe: false,
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withAffectedOptions)((0, shared_options_1.withTuiOptions)((0, shared_options_1.withRunOptions)((0, shared_options_1.withOutputStyleOption)((0, shared_options_1.withConfiguration)(yargs))))), 'affected'),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(args.verbose ?? process.env.NX_VERBOSE_LOGGING === 'true', async () => {
return (await (0, handle_import_1.handleImport)('./affected.js', __dirname)).affected('affected', {
...(0, shared_options_1.withOverrides)(args),
target: 'e2e',
});
});
process.exit(exitCode);
},
};

View File

@@ -0,0 +1,46 @@
/**
* Shared AI Agent NDJSON Output Utilities
*
* Base types and utilities for AI agent output across all Nx commands.
* Each command extends with its own specific progress stages, error codes, and result types.
*/
export type BaseProgressStage = 'starting' | 'complete' | 'error' | 'needs_input';
export interface ProgressMessage {
stage: string;
message: string;
}
export interface DetectedPlugin {
name: string;
reason: string;
}
export interface NextStep {
title: string;
command?: string;
url?: string;
note?: string;
}
export interface UserNextSteps {
description: string;
steps: NextStep[];
}
export interface PluginWarning {
plugin: string;
error: string;
hint: string;
}
/**
* Write NDJSON message to stdout.
* Only outputs if running under an AI agent.
* Each message is a single line of JSON.
*/
export declare function writeAiOutput(message: Record<string, any>): void;
/**
* Log progress stage.
* Only outputs if running under an AI agent.
*/
export declare function logProgress(stage: string, message: string): void;
/**
* Write detailed error information to a temp file for AI debugging.
* Returns the path to the error log file.
*/
export declare function writeErrorLog(error: Error | unknown, commandName?: string): string;

86
node_modules/nx/dist/src/command-line/ai/ai-output.js generated vendored Normal file
View File

@@ -0,0 +1,86 @@
"use strict";
/**
* Shared AI Agent NDJSON Output Utilities
*
* Base types and utilities for AI agent output across all Nx commands.
* Each command extends with its own specific progress stages, error codes, and result types.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeAiOutput = writeAiOutput;
exports.logProgress = logProgress;
exports.writeErrorLog = writeErrorLog;
const os_1 = require("os");
const path_1 = require("path");
const fs_1 = require("fs");
const native_1 = require("../../native");
/**
* Write NDJSON message to stdout.
* Only outputs if running under an AI agent.
* Each message is a single line of JSON.
*/
function writeAiOutput(message) {
if ((0, native_1.isAiAgent)()) {
process.stdout.write(JSON.stringify(message) + '\n');
// For success results, output plain text instructions that the agent can show the user
if (message.stage === 'complete' &&
'success' in message &&
message.success &&
'userNextSteps' in message) {
const steps = message.userNextSteps?.steps;
if (Array.isArray(steps)) {
let plainText = '\n---USER_NEXT_STEPS---\n';
plainText +=
'[DISPLAY] Show the user these next steps to complete setup:\n\n';
steps.forEach((step, i) => {
plainText += `${i + 1}. ${step.title}`;
if (step.command) {
plainText += `\n Run: ${step.command}`;
}
if (step.url) {
plainText += `\n Visit: ${step.url}`;
}
if (step.note) {
plainText += `\n ${step.note}`;
}
plainText += '\n';
});
plainText += '---END---\n';
process.stdout.write(plainText);
}
}
}
}
/**
* Log progress stage.
* Only outputs if running under an AI agent.
*/
function logProgress(stage, message) {
writeAiOutput({ stage, message });
}
/**
* Write detailed error information to a temp file for AI debugging.
* Returns the path to the error log file.
*/
function writeErrorLog(error, commandName = 'nx') {
const timestamp = Date.now();
const errorLogPath = (0, path_1.join)((0, os_1.tmpdir)(), `${commandName}-error-${timestamp}.log`);
let errorDetails = `Nx ${commandName} Error Log\n`;
errorDetails += `==================\n`;
errorDetails += `Timestamp: ${new Date(timestamp).toISOString()}\n\n`;
if (error instanceof Error) {
errorDetails += `Error: ${error.message}\n\n`;
if (error.stack) {
errorDetails += `Stack Trace:\n${error.stack}\n`;
}
}
else {
errorDetails += `Error: ${String(error)}\n`;
}
try {
(0, fs_1.writeFileSync)(errorLogPath, errorDetails);
}
catch {
return '';
}
return errorLogPath;
}

View File

@@ -0,0 +1,8 @@
import { CommandModule } from 'yargs';
export interface ConfigureAiAgentsOptions {
agents?: string[];
interactive?: boolean;
verbose?: boolean;
check?: boolean | 'outdated' | 'all';
}
export declare const yargsConfigureAiAgentsCommand: CommandModule<{}, ConfigureAiAgentsOptions>;

View File

@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsConfigureAiAgentsCommand = void 0;
const shared_options_1 = require("../yargs-utils/shared-options");
const handle_import_1 = require("../../utils/handle-import");
exports.yargsConfigureAiAgentsCommand = {
command: 'configure-ai-agents',
describe: 'Configure and update AI agent configurations for your workspace.',
builder: (yargs) => (0, shared_options_1.withVerbose)(yargs)
.option('agents', {
type: 'array',
string: true,
description: 'List of AI agents to set up.',
choices: ['claude', 'codex', 'copilot', 'cursor', 'gemini', 'opencode'],
})
.option('interactive', {
type: 'boolean',
description: 'When false disables interactive input prompts for options.',
default: true,
})
.option('check', {
type: 'string',
description: 'Check agent configurations. Use --check or --check=outdated to check only configured agents, or --check=all to include unconfigured/partial configurations. Does not make any changes.',
coerce: (value) => {
// --check (no value)
if (value === '')
return 'outdated';
// --check=true
if (value === 'true')
return 'outdated';
// --no-check or --check=false
if (value === 'false')
return false;
// --check=all or --check=outdated
return value;
},
choices: ['outdated', 'all'],
})
.example('$0 configure-ai-agents', 'Interactively select AI agents to update and configure')
.example('$0 configure-ai-agents --agents claude gemini', 'Prompts for updates and and configuration of Claude and Gemini AI agents')
.example('$0 configure-ai-agents --check', 'Checks if any configured agents are out of date and need to be updated')
.example('$0 configure-ai-agents --check=all', 'Checks if any agents are not configured, out of date or partially configured')
.example('$0 configure-ai-agents --agents claude gemini --no-interactive', 'Configures and updates Claude and Gemini AI agents without prompts'), // because of the coerce function
handler: async (args) => {
await (await (0, handle_import_1.handleImport)('./configure-ai-agents.js', __dirname)).configureAiAgentsHandler(args);
},
};

View File

@@ -0,0 +1,3 @@
import { ConfigureAiAgentsOptions } from './command-object';
export declare function configureAiAgentsHandler(args: ConfigureAiAgentsOptions, inner?: boolean): Promise<void>;
export declare function configureAiAgentsHandlerImpl(options: ConfigureAiAgentsOptions): Promise<void>;

View File

@@ -0,0 +1,425 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.configureAiAgentsHandler = configureAiAgentsHandler;
exports.configureAiAgentsHandlerImpl = configureAiAgentsHandlerImpl;
const tslib_1 = require("tslib");
const enquirer_1 = require("enquirer");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const pc = tslib_1.__importStar(require("picocolors"));
const constants_1 = require("../../ai/constants");
const detect_ai_agent_1 = require("../../ai/detect-ai-agent");
const utils_1 = require("../../ai/utils");
const client_1 = require("../../daemon/client/client");
const devkit_internals_1 = require("../../devkit-internals");
const output_1 = require("../../utils/output");
const package_manager_1 = require("../../utils/package-manager");
const provenance_1 = require("../../utils/provenance");
const versions_1 = require("../../utils/versions");
const workspace_root_1 = require("../../utils/workspace-root");
const handle_import_1 = require("../../utils/handle-import");
const ora = require("ora");
async function configureAiAgentsHandler(args, inner = false) {
// When called as inner from the tmp install, just run the impl directly
if (inner) {
return await configureAiAgentsHandlerImpl(args);
}
// Use environment variable to force local execution
if (process.env.NX_USE_LOCAL === 'true' ||
process.env.NX_AI_FILES_USE_LOCAL === 'true') {
await configureAiAgentsHandlerImpl(args);
await resetDaemonAgentStatus();
return;
}
// Skip downloading latest if the current version is already the latest
try {
const latestVersion = await (0, package_manager_1.resolvePackageVersionUsingRegistry)('nx', 'latest');
if (latestVersion === versions_1.nxVersion) {
return await configureAiAgentsHandlerImpl(args);
}
}
catch {
// If we can't check, proceed with download
}
let cleanup;
try {
await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest');
cleanup = packageInstallResults.cleanup;
let modulePath = require.resolve('nx/src/command-line/configure-ai-agents/configure-ai-agents.js', { paths: [packageInstallResults.tempDir] });
const module = await (0, handle_import_1.handleImport)(modulePath);
await module.configureAiAgentsHandler(args, true);
cleanup();
}
catch (error) {
if (cleanup) {
cleanup();
}
// Fall back to local implementation
await configureAiAgentsHandlerImpl(args);
}
// Reset daemon cache using the local daemon client (the inner handler's
// client belongs to the tmp install and isn't connected to our daemon)
await resetDaemonAgentStatus();
}
async function configureAiAgentsHandlerImpl(options) {
// Node 24 has stricter readline behavior, and enquirer is not checking for closed state
// when invoking operations, thus you get an ERR_USE_AFTER_CLOSE error.
process.on('uncaughtException', (error) => {
if (error &&
typeof error === 'object' &&
'code' in error &&
error['code'] === 'ERR_USE_AFTER_CLOSE')
return;
throw error;
});
const normalizedOptions = normalizeOptions(options);
const { nonConfiguredAgents, partiallyConfiguredAgents, fullyConfiguredAgents, disabledAgents, } = await (0, utils_1.getAgentConfigurations)(normalizedOptions.agents, workspace_root_1.workspaceRoot);
if (disabledAgents.length > 0) {
const commandNames = disabledAgents.map((a) => {
if (a.name === 'cursor')
return '"cursor"';
if (a.name === 'copilot')
return '"code"/"code-insiders"';
return a;
});
const title = commandNames.length === 1
? `${commandNames[0]} command not available.`
: `CLI commands ${commandNames
.map((c) => `${c}`)
.join('/')} not available.`;
output_1.output.log({
title,
bodyLines: [
pc.dim('To manually configure the Nx MCP in your editor, install Nx Console (https://nx.dev/getting-started/editor-setup)'),
],
});
}
if (normalizedOptions.agents.filter((agentName) => !disabledAgents.find((a) => a.name === agentName)).length === 0) {
output_1.output.error({
title: 'Please select at least one AI agent to configure.',
});
process.exit(1);
}
// important for wording
const usingAllAgents = normalizedOptions.agents.length === utils_1.supportedAgents.length;
if (normalizedOptions.check) {
const outOfDateAgents = fullyConfiguredAgents.filter((a) => a?.outdated);
// only error if something is fully configured but outdated
if (normalizedOptions.check === 'outdated') {
if (fullyConfiguredAgents.length === 0) {
output_1.output.log({
title: 'No AI agents are configured',
bodyLines: [
'You can configure AI agents by running `nx configure-ai-agents`.',
],
});
process.exit(0);
}
if (outOfDateAgents.length === 0) {
output_1.output.success({
title: 'All configured AI agents are up to date',
bodyLines: fullyConfiguredAgents.map((a) => `- ${a.displayName}`),
});
process.exit(0);
}
else {
output_1.output.log({
title: 'The following AI agents are out of date:',
bodyLines: [
...outOfDateAgents.map((a) => {
const rulesPath = a.rulesPath;
const displayPath = rulesPath.startsWith(workspace_root_1.workspaceRoot)
? (0, node_path_1.relative)(workspace_root_1.workspaceRoot, rulesPath)
: rulesPath;
return `- ${a.displayName} (${displayPath})`;
}),
'',
'You can update them by running `nx configure-ai-agents`.',
],
});
process.exit(1);
}
// error on any partial, outdated or non-configured agent
}
else if (normalizedOptions.check === 'all') {
if (partiallyConfiguredAgents.length === 0 &&
outOfDateAgents.length === 0 &&
nonConfiguredAgents.length === 0) {
output_1.output.success({
title: `All ${!usingAllAgents ? 'selected' : 'supported'} AI agents are fully configured and up to date`,
bodyLines: fullyConfiguredAgents.map((a) => `- ${a.displayName}`),
});
process.exit(0);
}
output_1.output.error({
title: 'The following agents are not fully configured or up to date:',
bodyLines: [
...partiallyConfiguredAgents,
...outOfDateAgents,
...nonConfiguredAgents,
].map((a) => getAgentChoiceForPrompt(a).message),
});
process.exit(1);
}
}
// Automatic mode (no explicit --agents): update outdated agents and report
// non-configured ones. When an AI agent is detected, also configure the
// detected agent itself (even if non-configured or partial).
const detectedAgent = (0, detect_ai_agent_1.detectAiAgent)();
const agentsExplicitlyPassed = options.agents !== undefined;
const isAutoMode = !agentsExplicitlyPassed && (options.interactive === false || detectedAgent);
if (isAutoMode) {
const agentsToConfig = [];
const allConfigs = [
...nonConfiguredAgents,
...partiallyConfiguredAgents,
...fullyConfiguredAgents,
];
// When an AI agent is detected, configure it if it needs it
if (detectedAgent) {
const detectedNeedsConfig = nonConfiguredAgents.some((a) => a.name === detectedAgent) ||
partiallyConfiguredAgents.some((a) => a.name === detectedAgent) ||
fullyConfiguredAgents.some((a) => a.name === detectedAgent && a.outdated);
if (detectedNeedsConfig) {
agentsToConfig.push(detectedAgent);
}
}
// Update any other outdated agents
for (const a of fullyConfiguredAgents) {
if (a.outdated && !agentsToConfig.includes(a.name)) {
agentsToConfig.push(a.name);
}
}
const stillNonConfigured = nonConfiguredAgents.filter((a) => !agentsToConfig.includes(a.name));
const nothingToDoMessage = detectedAgent
? `${utils_1.agentDisplayMap[detectedAgent] ?? detectedAgent} configuration is up to date`
: 'All configured AI agents are up to date';
if (agentsToConfig.length > 0) {
const configSpinner = ora(`Configuring agent(s)...`).start();
try {
await (0, utils_1.configureAgents)(agentsToConfig, workspace_root_1.workspaceRoot, false);
configSpinner.stop();
output_1.output.success({
title: 'AI agents configured successfully',
bodyLines: agentsToConfig.map((name) => {
const config = allConfigs.find((a) => a.name === name);
return config
? `${config.displayName}: ${getAgentConfiguredDescription(config)}`
: `- ${name}`;
}),
});
}
catch (e) {
configSpinner.fail('Failed to configure AI agents');
output_1.output.error({
title: 'Error details:',
bodyLines: [e.message],
});
process.exit(1);
}
}
else {
output_1.output.success({
title: nothingToDoMessage,
});
}
if (stillNonConfigured.length > 0) {
const agentNames = stillNonConfigured.map((a) => a.name);
output_1.output.log({
title: 'The following agents are not yet configured:',
bodyLines: [
...stillNonConfigured.map((a) => `- ${a.displayName}`),
'',
`Run: nx configure-ai-agents --agents ${agentNames.join(' ')}`,
],
});
}
return;
}
// Interactive mode (or non-interactive with explicit --agents)
const allAgentChoices = [];
const preselectedIndices = [];
let currentIndex = 0;
// Partially configured agents first (highest priority)
partiallyConfiguredAgents.forEach((a) => {
allAgentChoices.push(getAgentChoiceForPrompt(a));
preselectedIndices.push(currentIndex);
currentIndex++;
});
// Outdated agents second
for (const a of fullyConfiguredAgents) {
if (a.outdated) {
allAgentChoices.push(getAgentChoiceForPrompt(a));
preselectedIndices.push(currentIndex);
currentIndex++;
}
}
// Non-configured agents last
nonConfiguredAgents.forEach((a) => {
allAgentChoices.push(getAgentChoiceForPrompt(a));
currentIndex++;
});
if (allAgentChoices.length === 0) {
output_1.output.success({
title: `No new agents to configure. All ${!usingAllAgents ? 'selected' : 'supported'} AI agents are already configured:`,
bodyLines: fullyConfiguredAgents.map((agent) => `- ${agent.displayName}`),
});
process.exit(0);
}
let selectedAgents;
if (options.interactive !== false) {
try {
selectedAgents = (await (0, enquirer_1.prompt)({
type: 'multiselect',
name: 'agents',
message: 'Which AI agents would you like to configure? (space to select, enter to confirm)',
choices: allAgentChoices,
initial: preselectedIndices,
required: true,
footer: function () {
const focused = this.focused;
return pc.dim(` ${getAgentFooterDescription(focused.agentConfiguration)}`);
},
})).agents;
}
catch {
process.exit(1);
}
}
else {
// non-interactive with explicit --agents: configure all requested
selectedAgents = allAgentChoices.map((a) => a.name);
}
if (selectedAgents?.length === 0) {
output_1.output.log({
title: 'No agents selected',
});
process.exit(0);
}
const configSpinner = ora(`Configuring agent(s)...`).start();
try {
await (0, utils_1.configureAgents)(selectedAgents, workspace_root_1.workspaceRoot, false);
// Combine all agent configurations for display
const allAgentConfigs = [
...nonConfiguredAgents,
...partiallyConfiguredAgents,
...fullyConfiguredAgents,
];
const configuredOrUpdatedAgents = allAgentConfigs.filter((a) => selectedAgents.includes(a.name) ||
fullyConfiguredAgents.some((f) => f.name === a.name));
configSpinner.stop();
output_1.output.success({
title: 'AI agents configured successfully',
bodyLines: configuredOrUpdatedAgents.map((agent) => `${agent.displayName}: ${getAgentConfiguredDescription(agent)}`),
});
return;
}
catch (e) {
configSpinner.fail('Failed to set up AI agents');
output_1.output.error({
title: 'Error details:',
bodyLines: [e.message],
});
process.exit(1);
}
}
/**
* Get the verbose footer description for an agent.
* Describes the end state per agent type.
*/
function getAgentFooterDescription(agent) {
// Extract filename from rulesPath
const rulesFile = agent.rulesPath.split('/').pop() || 'AGENTS.md';
switch (agent.name) {
case 'claude': {
let description = `Installs Nx plugin (MCP + skills + agents). Updates ${rulesFile}.`;
// Check if .mcp.json exists with nx-mcp - if so, mention cleanup
const mcpJsonPath = (0, constants_1.claudeMcpJsonPath)(workspace_root_1.workspaceRoot);
if ((0, node_fs_1.existsSync)(mcpJsonPath)) {
try {
const mcpJsonContents = JSON.parse((0, node_fs_1.readFileSync)(mcpJsonPath, 'utf-8'));
if (mcpJsonContents?.mcpServers?.['nx-mcp']) {
description +=
' Removes nx-mcp from .mcp.json (now handled by plugin).';
}
}
catch {
// Ignore errors reading .mcp.json
}
}
return description;
}
case 'cursor':
case 'copilot':
return `Installs Nx Console (MCP). Adds skills and agents. Updates ${rulesFile}.`;
case 'gemini':
case 'opencode':
return `Configures MCP server. Adds skills and agents. Updates ${rulesFile}.`;
case 'codex':
return `Configures MCP server. Adds skills. Updates ${rulesFile}.`;
default:
return '';
}
}
/**
* Get a compact description of what was configured for an agent.
* Used in the post-configuration output.
*/
function getAgentConfiguredDescription(agent) {
// Extract filename from rulesPath
const rulesFile = agent.rulesPath.split('/').pop() || 'AGENTS.md';
switch (agent.name) {
case 'claude':
return `Nx plugin (MCP + skills + agents) + ${rulesFile}`;
case 'cursor':
case 'copilot':
return `Nx Console (MCP) + skills + ${rulesFile}`;
case 'gemini':
case 'opencode':
return `MCP + skills + ${rulesFile}`;
case 'codex':
return `MCP + skills + ${rulesFile}`;
default:
return '';
}
}
function getAgentChoiceForPrompt(agent) {
const partiallyConfigured = agent.mcp !== agent.rules;
const needsUpdate = partiallyConfigured || agent.outdated;
return {
name: agent.name,
message: needsUpdate
? `${agent.displayName} (update available)`
: agent.displayName,
agentConfiguration: agent,
};
}
function normalizeOptions(options) {
const agents = (options.agents ?? utils_1.supportedAgents).filter((a) => utils_1.supportedAgents.includes(a));
// it used to be just --check which was implicitly 'outdated'
const check = (options.check === true ? 'outdated' : options.check) ?? false;
return {
...options,
agents,
check,
};
}
async function resetDaemonAgentStatus() {
try {
// Don't check daemonClient.enabled() — the CLI sets NX_DAEMON=false for
// configure-ai-agents (it doesn't need the daemon to do its work), but a
// daemon started by a previous command may still be running and serving
// cached status. We just need to reach it to reset its cache.
if (await client_1.daemonClient.isServerAvailable()) {
await client_1.daemonClient.resetConfigureAiAgentsStatus();
}
}
catch {
// Daemon may not be running, that's fine
}
finally {
// Close the daemon socket so the process can exit cleanly.
client_1.daemonClient.reset();
}
}

View File

@@ -0,0 +1,3 @@
export declare const yargsDaemonCommand: import("../yargs-utils/arguments-of").CommandModule<{}, {
verbose: boolean;
}>;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsDaemonCommand = void 0;
const documentation_1 = require("../yargs-utils/documentation");
const handle_errors_1 = require("../../utils/handle-errors");
const handle_import_1 = require("../../utils/handle-import");
const shared_options_1 = require("../yargs-utils/shared-options");
const arguments_of_1 = require("../yargs-utils/arguments-of");
const builder = (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withVerbose)(withDaemonOptions(yargs)), 'daemon');
exports.yargsDaemonCommand = (0, arguments_of_1.makeCommandModule)({
command: 'daemon',
describe: 'Prints information about the Nx Daemon process or starts a daemon process.',
builder,
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(args.verbose, async () => (await (0, handle_import_1.handleImport)('./daemon.js', __dirname)).daemonHandler(args));
process.exit(exitCode);
},
});
function withDaemonOptions(yargs) {
return yargs
.option('start', {
type: 'boolean',
default: false,
})
.option('stop', {
type: 'boolean',
default: false,
});
}

View File

@@ -0,0 +1,2 @@
import type { Arguments } from 'yargs';
export declare function daemonHandler(args: Arguments): Promise<void>;

31
node_modules/nx/dist/src/command-line/daemon/daemon.js generated vendored Normal file
View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.daemonHandler = daemonHandler;
const cache_1 = require("../../daemon/cache");
const tmp_dir_1 = require("../../daemon/tmp-dir");
const handle_import_1 = require("../../utils/handle-import");
const output_1 = require("../../utils/output");
async function daemonHandler(args) {
const { daemonClient } = await (0, handle_import_1.handleImport)('../../daemon/client/client.js', __dirname);
if (args.start) {
const pid = await daemonClient.startInBackground();
output_1.output.log({
title: `Daemon Server - Started in a background process...`,
bodyLines: [
`${output_1.output.dim('Logs from the Daemon process (')}ID: ${pid}${output_1.output.dim(') can be found here:')} ${tmp_dir_1.DAEMON_OUTPUT_LOG_FILE}\n`,
],
});
}
else if (args.stop) {
await daemonClient.stop();
output_1.output.log({ title: 'Daemon Server - Stopped' });
}
else if (await daemonClient.isServerAvailable()) {
const pid = (0, cache_1.getDaemonProcessIdSync)();
console.log(`Nx Daemon is currently running:
- Logs: ${tmp_dir_1.DAEMON_OUTPUT_LOG_FILE}${pid ? `\n - Process ID: ${pid}` : ''}`);
}
else {
console.log('Nx Daemon is not running.');
}
}

View File

@@ -0,0 +1,9 @@
import { CommandModule } from 'yargs';
/**
* @deprecated 'Use `nx graph --affected`, or` nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 19.'
*/
export declare const yargsAffectedGraphCommand: CommandModule;
/**
* @deprecated 'Use `nx show --affected`, `nx affected --graph` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 19.'
*/
export declare const yargsPrintAffectedCommand: CommandModule;

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsPrintAffectedCommand = exports.yargsAffectedGraphCommand = void 0;
const handle_errors_1 = require("../../utils/handle-errors");
const shared_options_1 = require("../yargs-utils/shared-options");
const command_object_1 = require("../graph/command-object");
const affectedGraphDeprecationMessage = 'Use `nx graph --affected`, or `nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command has been removed in Nx 19.';
const printAffectedDeprecationMessage = 'Use `nx show projects --affected`, `nx affected --graph -t build` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command has been removed in Nx 19.';
/**
* @deprecated 'Use `nx graph --affected`, or` nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 19.'
*/
exports.yargsAffectedGraphCommand = {
command: 'affected:graph',
describe: false,
aliases: ['affected:dep-graph'],
builder: (yargs) => (0, shared_options_1.withAffectedOptions)((0, command_object_1.withGraphOptions)(yargs)),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(false, () => {
throw new Error(affectedGraphDeprecationMessage);
});
process.exit(exitCode);
},
deprecated: affectedGraphDeprecationMessage,
};
/**
* @deprecated 'Use `nx show --affected`, `nx affected --graph` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 19.'
*/
exports.yargsPrintAffectedCommand = {
command: 'print-affected',
describe: false,
builder: (yargs) => (0, shared_options_1.withAffectedOptions)((0, shared_options_1.withTargetAndConfigurationOption)(yargs, false))
.option('select', {
type: 'string',
describe: 'Select the subset of the returned json document (e.g., --select=projects).',
})
.option('type', {
type: 'string',
choices: ['app', 'lib'],
describe: 'Select the type of projects to be returned (e.g., --type=app).',
}),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(false, () => {
throw new Error(printAffectedDeprecationMessage);
});
process.exit(exitCode);
},
deprecated: printAffectedDeprecationMessage,
};

12
node_modules/nx/dist/src/command-line/examples.d.ts generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export interface Example {
command: string;
description: string;
}
export interface CliDocsCommandMetadata {
supportedVersionRange?: string;
}
/**
* Docs-only metadata keyed by the full command name as rendered in the CLI docs.
*/
export declare const cliDocsCommandMetadata: Record<string, CliDocsCommandMetadata>;
export declare const examples: Record<string, Example[]>;

403
node_modules/nx/dist/src/command-line/examples.js generated vendored Normal file
View File

@@ -0,0 +1,403 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.examples = exports.cliDocsCommandMetadata = void 0;
/**
* Docs-only metadata keyed by the full command name as rendered in the CLI docs.
*/
exports.cliDocsCommandMetadata = {
'show target': {
supportedVersionRange: 'Nx 22.6+',
},
};
exports.examples = {
affected: [
{
command: 'affected -t custom-target',
description: 'Run custom target for all affected projects',
},
{
command: 'affected -t test --parallel=5',
description: 'Run tests in parallel',
},
{
command: 'affected -t lint test build',
description: 'Run lint, test, and build targets for affected projects. Requires Nx v15.4+',
},
{
command: 'affected -t test --files=libs/mylib/src/index.ts',
description: 'Run tests for all the projects affected by changing the index.ts file',
},
{
command: 'affected -t test --base=main --head=HEAD',
description: 'Run tests for all the projects affected by the changes between main and HEAD (e.g., PR)',
},
{
command: 'affected -t test --base=main~1 --head=main',
description: 'Run tests for all the projects affected by the last commit on main',
},
{
command: "affected -t=build --exclude='*,!tag:dotnet'",
description: 'Run build for only projects with the tag `dotnet`',
},
{
command: 'affected -t build --tag=$NX_TASK_TARGET_PROJECT:latest',
description: 'Use the currently executing project name in your command',
},
{
command: 'affected -t=build --graph',
description: 'Preview the task graph that Nx would run inside a webview',
},
{
command: 'affected -t=build --graph=output.json',
description: 'Save the task graph to a file',
},
{
command: 'affected -t=build --graph=stdout',
description: 'Print the task graph to the console',
},
],
'affected:test': [
{
command: 'affected:test --parallel=5',
description: 'Run tests in parallel',
},
{
command: 'affected:test --files=libs/mylib/src/index.ts',
description: 'Run tests for all the projects affected by changing the index.ts file',
},
{
command: 'affected:test --base=main --head=HEAD',
description: 'Run tests for all the projects affected by the changes between main and HEAD (e.g., PR)',
},
{
command: 'affected:test --base=main~1 --head=main',
description: 'Run tests for all the projects affected by the last commit on main',
},
],
'affected:build': [
{
command: 'affected:build --parallel=5',
description: 'Run build in parallel',
},
{
command: 'affected:build --files=libs/mylib/src/index.ts',
description: 'Run build for all the projects affected by changing the index.ts file',
},
{
command: 'affected:build --base=main --head=HEAD',
description: 'Run build for all the projects affected by the changes between main and HEAD (e.g., PR)',
},
{
command: 'affected:build --base=main~1 --head=main',
description: 'Run build for all the projects affected by the last commit on main',
},
],
'affected:e2e': [
{
command: 'affected:e2e --parallel=5',
description: 'Run tests in parallel',
},
{
command: 'affected:e2e --files=libs/mylib/src/index.ts',
description: 'Run tests for all the projects affected by changing the index.ts file',
},
{
command: 'affected:e2e --base=main --head=HEAD',
description: 'Run tests for all the projects affected by the changes between main and HEAD (e.g., PR)',
},
{
command: 'affected:e2e --base=main~1 --head=main',
description: 'Run tests for all the projects affected by the last commit on main',
},
],
'affected:lint': [
{
command: 'affected:lint --parallel=5',
description: 'Run lint in parallel',
},
{
command: 'affected:lint --files=libs/mylib/src/index.ts',
description: 'Run lint for all the projects affected by changing the index.ts file',
},
{
command: 'affected:lint --base=main --head=HEAD',
description: 'Run lint for all the projects affected by the changes between main and HEAD (e.g., PR)',
},
{
command: 'affected:lint --base=main~1 --head=main',
description: 'Run lint for all the projects affected by the last commit on main',
},
],
'format:write': [],
'format:check': [],
graph: [
{
command: 'graph',
description: 'Open the project graph of the workspace in the browser',
},
{
command: 'graph --file=output.json',
description: 'Save the project graph into a json file',
},
{
command: 'graph --file=output.html',
description: 'Generate a static website with project graph into an html file, accompanied by an asset folder called static',
},
{
command: 'graph --print',
description: 'Print the project graph as JSON to the console',
},
{
command: 'graph --focus=todos-feature-main',
description: 'Show the graph where every node is either an ancestor or a descendant of todos-feature-main',
},
{
command: 'graph --exclude=project-one,project-two',
description: 'Exclude project-one and project-two from the project graph',
},
{
command: 'graph --focus=todos-feature-main --exclude=project-one,project-two',
description: 'Show the graph where every node is either an ancestor or a descendant of todos-feature-main, but exclude project-one and project-two',
},
{
command: 'graph --watch',
description: 'Watch for changes to project graph and update in-browser',
},
],
list: [
{
command: 'list',
description: 'List the plugins installed in the current workspace',
},
{
command: 'list @nx/web',
description: 'List the generators and executors available in the `@nx/web` plugin if it is installed (If the plugin is not installed `nx` will show advice on how to add it to your workspace)',
},
],
'run-many': [
{
command: 'run-many -t test',
description: 'Test all projects',
},
{
command: 'run-many -t test -p proj1 proj2',
description: 'Test proj1 and proj2 in parallel',
},
{
command: 'run-many -t test -p proj1 proj2 --parallel=5',
description: 'Test proj1 and proj2 in parallel using 5 workers',
},
{
command: 'run-many -t test -p proj1 proj2 --parallel=false',
description: 'Test proj1 and proj2 in sequence',
},
{
command: 'run-many -t test --projects=*-app --exclude excluded-app',
description: 'Test all projects ending with `*-app` except `excluded-app`. Note: your shell may require you to escape the `*` like this: `\\*`',
},
{
command: 'run-many -t test --projects=tag:api-*',
description: 'Test all projects with tags starting with `api-`. Note: your shell may require you to escape the `*` like this: `\\*`',
},
{
command: 'run-many -t test --projects=tag:type:ui',
description: 'Test all projects with a `type:ui` tag',
},
{
command: 'run-many -t test --projects=tag:type:feature,tag:type:ui',
description: 'Test all projects with a `type:feature` or `type:ui` tag',
},
{
command: 'run-many --targets=lint,test,build',
description: 'Run lint, test, and build targets for all projects. Requires Nx v15.4+',
},
{
command: 'run-many -t=build --graph',
description: 'Preview the task graph that Nx would run inside a webview',
},
{
command: 'run-many -t=build --graph=output.json',
description: 'Save the task graph to a file',
},
{
command: 'run-many -t=build --graph=stdout',
description: 'Print the task graph to the console',
},
],
run: [
{
command: 'run myapp:build',
description: 'Run the target build for the myapp project',
},
{
command: 'run myapp:build:production',
description: 'Run the target build for the myapp project, with production configuration',
},
{
command: 'run myapp:build --graph',
description: 'Preview the task graph that Nx would run inside a webview',
},
{
command: 'run myapp:build --graph=output.json',
description: 'Save the task graph to a file',
},
{
command: 'run myapp:build --graph=stdout',
description: 'Print the task graph to the console',
},
{
command: 'run myapp:"build:test"',
description: 'Run\'s a target named build:test for the myapp project. Note the quotes around the target name to prevent "test" from being considered a configuration',
},
],
migrate: [
{
command: 'migrate latest',
description: 'Update all Nx plugins to "latest". This will generate migrations.json',
},
{
command: 'migrate 9.0.0',
description: 'Update all Nx plugins to "9.0.0". This will generate migrations.json',
},
{
command: 'migrate @nx/workspace@9.0.0 --from="@nx/workspace@8.0.0,@nx/node@8.0.0"',
description: 'Update @nx/workspace and generate the list of migrations starting with version 8.0.0 of @nx/workspace and @nx/node, regardless of what is installed locally',
},
{
command: 'migrate @nx/workspace@9.0.0 --to="@nx/react@9.0.1,@nx/angular@9.0.1"',
description: 'Update @nx/workspace to "9.0.0". If it tries to update @nx/react or @nx/angular, use version "9.0.1"',
},
{
command: 'migrate another-package@12.0.0',
description: 'Update another-package to "12.0.0". This will update other packages and will generate migrations.json file',
},
{
command: 'migrate latest --interactive',
description: 'Collect package updates and migrations in interactive mode. In this mode, the user will be prompted whether to apply any optional package update and migration',
},
{
command: 'migrate latest --from=nx@14.5.0 --exclude-applied-migrations',
description: 'Collect package updates and migrations starting with version 14.5.0 of "nx" (and Nx first-party plugins), regardless of what is installed locally, while excluding migrations that should have been applied on previous updates',
},
{
command: 'migrate --run-migrations=migrations.json',
description: 'Run migrations from the provided migrations.json file. You can modify migrations.json and run this command many times',
},
{
command: 'migrate --run-migrations --create-commits',
description: 'Create a dedicated commit for each successfully completed migration. You can customize the prefix used for each commit by additionally setting --commit-prefix="PREFIX_HERE "',
},
],
reset: [
{
command: 'reset',
description: 'Clears the internal state of the daemon and metadata that Nx is tracking. Helpful if you are getting strange errors and want to start fresh',
},
{
command: 'reset --only-cache',
description: 'Clears the Nx Cache directory. This will remove all local cache entries for tasks, but will not affect the remote cache',
},
{
command: 'reset --only-daemon',
description: 'Stops the Nx Daemon, it will be restarted fresh when the next Nx command is run.',
},
{
command: 'reset --only-workspace-data',
description: 'Clears the workspace data directory. Used by Nx to store cached data about the current workspace (e.g. partial results, incremental data, etc)',
},
],
show: [
{
command: 'show projects',
description: 'Show all projects in the workspace',
},
{
command: 'show projects --projects api-*',
description: 'Show all projects with names starting with "api-". The "projects" option is useful to see which projects would be selected by run-many',
},
{
command: 'show projects --projects tag:ui-*',
description: 'Show all projects with a tag starting with "ui-". The "projects" option is useful to see which projects would be selected by run-many',
},
{
command: 'show projects --with-target serve',
description: 'Show all projects with a serve target',
},
{
command: 'show projects --affected',
description: 'Show affected projects in the workspace',
},
{
command: 'show projects --affected --type app',
description: 'Show affected apps in the workspace',
},
{
command: 'show projects --affected --exclude=*-e2e',
description: 'Show affected projects in the workspace, excluding end-to-end projects',
},
{
command: 'show project my-app',
description: 'If in an interactive terminal, opens the project detail view. If not in an interactive terminal, defaults to JSON',
},
{
command: 'show project my-app --json',
description: 'Show detailed information about "my-app" in a json format',
},
{
command: 'show project my-app --json false',
description: 'Show information about "my-app" in a human readable format',
},
{
command: 'show project my-app --web',
description: 'Opens a web browser to explore the configuration of "my-app"',
},
{
command: 'show target my-app:build',
description: 'Prints the specified + inferred configuration for `my-app:build`',
},
{
command: 'show target my-app:build inputs',
description: 'Prints the resolved inputs for `my-app:build`',
},
{
command: 'show target my-app:build inputs --check packages/my-app/index.html',
description: 'Checks if `packages/my-app/index.html` is an input for `my-app:build`',
},
{
command: 'show target my-app:build outputs',
description: 'Prints the outputs detected on disk for `my-app:build`',
},
{
command: 'show target my-app:build outputs --check packages/my-app/dist/index.html',
description: 'Checks if `packages/my-app/dist/index.html` is an output for `my-app:build`',
},
],
watch: [
{
command: 'watch --projects=app -- echo \\$NX_PROJECT_NAME \\$NX_FILE_CHANGES',
description: 'Watch the "app" project and echo the project name and the files that changed',
},
{
command: 'watch --projects=app1,app2 --includeDependentProjects -- echo \\$NX_PROJECT_NAME',
description: 'Watch "app1" and "app2" and echo the project name whenever a specified project or its dependencies change',
},
{
command: 'watch --all -- echo \\$NX_PROJECT_NAME',
description: 'Watch all projects (including newly created projects) in the workspace',
},
],
add: [
{
command: 'add @nx/react',
description: 'Install the `@nx/react` package matching the installed version of the `nx` package and run its `@nx/react:init` generator',
},
{
command: 'add non-core-nx-plugin',
description: 'Install the latest version of the `non-core-nx-plugin` package and run its `non-core-nx-plugin:init` generator if available',
},
{
command: 'add @nx/react@17.0.0',
description: 'Install version `17.0.0` of the `@nx/react` package and run its `@nx/react:init` generator',
},
],
};

View File

@@ -0,0 +1,2 @@
import { CommandModule } from 'yargs';
export declare const yargsExecCommand: CommandModule;

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsExecCommand = void 0;
const shared_options_1 = require("../yargs-utils/shared-options");
const handle_import_1 = require("../../utils/handle-import");
exports.yargsExecCommand = {
command: 'exec',
describe: 'Executes any command as if it was a target on the project.',
builder: (yargs) => (0, shared_options_1.withTuiOptions)((0, shared_options_1.withRunManyOptions)(yargs)),
handler: async (args) => {
try {
await (await (0, handle_import_1.handleImport)('./exec.js', __dirname)).nxExecCommand((0, shared_options_1.withOverrides)(args));
process.exit(0);
}
catch (e) {
console.error(e);
process.exit(1);
}
},
};

1
node_modules/nx/dist/src/command-line/exec/exec.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export declare function nxExecCommand(args: Record<string, string | string[] | boolean>): Promise<unknown>;

143
node_modules/nx/dist/src/command-line/exec/exec.js generated vendored Normal file
View File

@@ -0,0 +1,143 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nxExecCommand = nxExecCommand;
const tslib_1 = require("tslib");
const child_process_1 = require("child_process");
const path_1 = require("path");
const process_1 = require("process");
const yargs_parser_1 = tslib_1.__importDefault(require("yargs-parser"));
const fs_1 = require("fs");
const find_matching_projects_1 = require("../../utils/find-matching-projects");
const configuration_1 = require("../../config/configuration");
const project_graph_1 = require("../../project-graph/project-graph");
const command_line_utils_1 = require("../../utils/command-line-utils");
const fileutils_1 = require("../../utils/fileutils");
const output_1 = require("../../utils/output");
const package_manager_1 = require("../../utils/package-manager");
const workspace_root_1 = require("../../utils/workspace-root");
const path_2 = require("../../utils/path");
const calculate_default_project_name_1 = require("../../config/calculate-default-project-name");
const get_command_projects_1 = require("../../commands-runner/get-command-projects");
async function nxExecCommand(args) {
const nxJson = (0, configuration_1.readNxJson)();
const { nxArgs, overrides } = (0, command_line_utils_1.splitArgsIntoNxArgsAndOverrides)(args, 'run-many', { printWarnings: args.graph !== 'stdout' }, nxJson);
const scriptArgV = readScriptArgV(overrides);
const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
// NX is already running
if (process.env.NX_TASK_TARGET_PROJECT) {
const command = scriptArgV
.reduce((cmd, arg) => cmd + `"${arg}" `, '')
.trim();
(0, child_process_1.execSync)(command, {
stdio: 'inherit',
env: {
...process.env,
NX_PROJECT_NAME: process.env.NX_TASK_TARGET_PROJECT,
NX_PROJECT_ROOT_PATH: projectGraph.nodes?.[process.env.NX_TASK_TARGET_PROJECT]?.data?.root,
},
windowsHide: true,
});
}
else {
// nx exec is being ran inside of Nx's context
return runScriptAsNxTarget(projectGraph, scriptArgV, nxArgs);
}
}
async function runScriptAsNxTarget(projectGraph, argv, nxArgs) {
// NPM, Yarn, and PNPM set this to the name of the currently executing script. Lets use it if we can.
const targetName = process.env.npm_lifecycle_event;
if (targetName) {
const defaultPorject = getDefaultProject(projectGraph);
const scriptDefinition = getScriptDefinition(targetName, defaultPorject);
if (scriptDefinition) {
runTargetOnProject(scriptDefinition, targetName, defaultPorject, defaultPorject.name, argv);
return;
}
}
const projects = getProjects(projectGraph, nxArgs);
const projectsToRun = (0, get_command_projects_1.getCommandProjects)(projectGraph, projects, nxArgs);
projectsToRun.forEach((projectName) => {
const command = argv.reduce((cmd, arg) => cmd + `"${arg}" `, '').trim();
(0, child_process_1.execSync)(command, {
stdio: 'inherit',
env: {
...process.env,
NX_PROJECT_NAME: projectGraph.nodes?.[projectName]?.name,
NX_PROJECT_ROOT_PATH: projectGraph.nodes?.[projectName]?.data?.root,
},
cwd: projectGraph.nodes?.[projectName]?.data?.root
? (0, path_2.joinPathFragments)(workspace_root_1.workspaceRoot, projectGraph.nodes?.[projectName]?.data?.root)
: workspace_root_1.workspaceRoot,
windowsHide: true,
});
});
}
function runTargetOnProject(scriptDefinition, targetName, project, projectName, argv) {
ensureNxTarget(project, targetName);
// Get ArgV that is provided in npm script definition
const providedArgs = (0, yargs_parser_1.default)(scriptDefinition)._.slice(2);
const extraArgs = providedArgs.length === argv.length ? [] : argv.slice(providedArgs.length);
const pm = (0, package_manager_1.getPackageManagerCommand)();
// `targetName` might be an npm script with `:` like: `start:dev`, `start:debug`.
const command = `${pm.exec} nx run ${projectName}:\\\"${targetName}\\\" ${extraArgs.join(' ')}`;
(0, child_process_1.execSync)(command, {
stdio: 'inherit',
windowsHide: true,
});
}
function readScriptArgV(overrides) {
const scriptSeparatorIdx = process.argv.findIndex((el) => el === '--');
if (scriptSeparatorIdx === -1) {
output_1.output.error({
title: '`nx exec` requires passing in a command after `--`',
});
process.exit(1);
}
return overrides.__overrides_unparsed__;
}
function getScriptDefinition(targetName, project) {
if (!project) {
return;
}
const packageJsonPath = (0, path_1.join)(workspace_root_1.workspaceRoot, project.data.root, 'package.json');
if ((0, fs_1.existsSync)(packageJsonPath)) {
const scriptDefinition = (0, fileutils_1.readJsonFile)(packageJsonPath).scripts?.[targetName];
return scriptDefinition;
}
}
function ensureNxTarget(project, targetName) {
if (!project.data.targets[targetName]) {
output_1.output.error({
title: `Nx cannot find a target called "${targetName}" for ${project.name}`,
bodyLines: [
`Is ${targetName} missing from ${project.data.root}/package.json's nx.includedScripts field?`,
],
});
(0, process_1.exit)(1);
}
}
function getDefaultProject(projectGraph) {
const defaultProjectName = (0, calculate_default_project_name_1.calculateDefaultProjectName)(process.cwd(), workspace_root_1.workspaceRoot, (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph), (0, configuration_1.readNxJson)());
if (defaultProjectName && projectGraph.nodes[defaultProjectName]) {
return projectGraph.nodes[defaultProjectName];
}
}
function getProjects(projectGraph, nxArgs) {
let selectedProjects = {};
// get projects matched
if (nxArgs.projects?.length) {
const matchingProjects = (0, find_matching_projects_1.findMatchingProjects)(nxArgs.projects, projectGraph.nodes);
for (const project of matchingProjects) {
selectedProjects[project] = projectGraph.nodes[project];
}
}
else {
// if no project specified, return all projects
selectedProjects = { ...projectGraph.nodes };
}
const excludedProjects = (0, find_matching_projects_1.findMatchingProjects)(nxArgs.exclude, selectedProjects);
for (const excludedProject of excludedProjects) {
delete selectedProjects[excludedProject];
}
return Object.values(selectedProjects);
}

View File

@@ -0,0 +1,3 @@
import { CommandModule } from 'yargs';
export declare const yargsFormatCheckCommand: CommandModule;
export declare const yargsFormatWriteCommand: CommandModule;

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsFormatWriteCommand = exports.yargsFormatCheckCommand = void 0;
const handle_import_1 = require("../../utils/handle-import");
const documentation_1 = require("../yargs-utils/documentation");
const shared_options_1 = require("../yargs-utils/shared-options");
exports.yargsFormatCheckCommand = {
command: 'format:check',
describe: 'Check for un-formatted files.',
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)(withFormatOptions(yargs), 'format:check'),
handler: async (args) => {
await (await (0, handle_import_1.handleImport)('./format.js', __dirname)).format('check', args);
process.exit(0);
},
};
exports.yargsFormatWriteCommand = {
command: 'format:write',
describe: 'Overwrite un-formatted files.',
aliases: ['format'],
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)(withFormatOptions(yargs), 'format:write'),
handler: async (args) => {
await (await (0, handle_import_1.handleImport)('./format.js', __dirname)).format('write', args);
process.exit(0);
},
};
function withFormatOptions(yargs) {
return (0, shared_options_1.withAffectedOptions)(yargs)
.parserConfiguration({
'camel-case-expansion': true,
})
.option('libs-and-apps', {
describe: 'Format only libraries and applications files.',
type: 'boolean',
})
.option('projects', {
describe: 'Projects to format (comma/space delimited).',
type: 'string',
coerce: shared_options_1.parseCSV,
})
.option('sort-root-tsconfig-paths', {
describe: `Ensure the workspace's tsconfig compilerOptions.paths are sorted. Warning: This will cause comments in the tsconfig to be lost. The default value is "false" unless NX_FORMAT_SORT_TSCONFIG_PATHS is set to "true".`,
type: 'boolean',
})
.option('all', {
describe: 'Format all projects.',
type: 'boolean',
})
.conflicts({
all: 'projects',
})
.middleware((args) => {
args.sortRootTsconfigPaths ??=
process.env.NX_FORMAT_SORT_TSCONFIG_PATHS === 'true';
// If NX_FORMAT_SORT_TSCONFIG_PATHS=false and --sort-root-tsconfig-paths is passed, we want to set it to true favoring the arg
process.env.NX_FORMAT_SORT_TSCONFIG_PATHS =
args.sortRootTsconfigPaths.toString();
});
}

View File

@@ -0,0 +1,2 @@
import * as yargs from 'yargs';
export declare function format(command: 'check' | 'write', args: yargs.Arguments): Promise<void>;

233
node_modules/nx/dist/src/command-line/format/format.js generated vendored Normal file
View File

@@ -0,0 +1,233 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.format = format;
const tslib_1 = require("tslib");
const node_child_process_1 = require("node:child_process");
const path = tslib_1.__importStar(require("node:path"));
const semver_1 = require("semver");
const configuration_1 = require("../../config/configuration");
const typescript_1 = require("../../plugins/js/utils/typescript");
const affected_project_graph_1 = require("../../project-graph/affected/affected-project-graph");
const file_utils_1 = require("../../project-graph/file-utils");
const project_graph_1 = require("../../project-graph/project-graph");
const chunkify_1 = require("../../utils/chunkify");
const command_line_utils_1 = require("../../utils/command-line-utils");
const fileutils_1 = require("../../utils/fileutils");
const handle_import_1 = require("../../utils/handle-import");
const ignore_1 = require("../../utils/ignore");
const object_sort_1 = require("../../utils/object-sort");
const output_1 = require("../../utils/output");
const package_json_1 = require("../../utils/package-json");
const workspace_root_1 = require("../../utils/workspace-root");
async function format(command, args) {
let prettier;
try {
prettier = await (0, handle_import_1.handleImport)('prettier');
}
catch {
output_1.output.error({
title: 'Prettier is not installed.',
bodyLines: [
`Please install "prettier" and try again, or don't run the "nx format:${command}" command.`,
],
});
process.exit(1);
}
const { nxArgs } = (0, command_line_utils_1.splitArgsIntoNxArgsAndOverrides)(args, 'affected', { printWarnings: false }, (0, configuration_1.readNxJson)());
const patterns = (await getPatterns(prettier, { ...args, ...nxArgs })).map((p) => {
// On non-Windows, escape $ to prevent shell variable interpolation
// (the shell consumes one \, so \\$ becomes \$ which the shell treats as literal $)
// On Windows (cmd.exe), $ is not a special character, so escaping it would
// cause prettier to look for a file with a literal \$ in the name
// prettier-ignore
const escaped = process.platform !== 'win32' ? p.replace(/\$/g, '\\\$') : p;
return `"${escaped}"`;
});
// Chunkify the patterns array to prevent crashing the windows terminal
const chunkList = (0, chunkify_1.chunkify)(patterns);
switch (command) {
case 'write':
if (nxArgs.sortRootTsconfigPaths) {
sortTsConfig();
}
addRootConfigFiles(chunkList, nxArgs);
chunkList.forEach((chunk) => write(prettier, chunk));
break;
case 'check': {
const filesWithDifferentFormatting = [];
for (const chunk of chunkList) {
const files = await check(chunk);
filesWithDifferentFormatting.push(...files);
}
if (filesWithDifferentFormatting.length > 0) {
if (nxArgs.verbose) {
output_1.output.error({
title: 'The following files are not formatted correctly based on your Prettier configuration',
bodyLines: [
'- Run "nx format:write" and commit the resulting diff to fix these files.',
'- Please note, Prettier does not support a native way to diff the output of its check logic (https://github.com/prettier/prettier/issues/6885).',
'',
...filesWithDifferentFormatting,
],
});
}
else {
console.log(filesWithDifferentFormatting.join('\n'));
}
process.exit(1);
}
break;
}
}
}
async function getPatterns(prettier, args) {
const allFilesPattern = ['.'];
if (args.all) {
return allFilesPattern;
}
try {
if (args.projects && args.projects.length > 0) {
const graph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
return getPatternsFromProjects(args.projects, graph);
}
const p = (0, command_line_utils_1.parseFiles)(args);
const supportedExtensions = new Set((await prettier.getSupportInfo()).languages
.flatMap((language) => language.extensions)
.filter((extension) => !!extension)
// Prettier supports ".swcrc" as a file instead of an extension
// So we add ".swcrc" as a supported extension manually
// which allows it to be considered for calculating "patterns"
.concat('.swcrc'));
const patterns = p.files
.map((f) => path.relative(workspace_root_1.workspaceRoot, f))
.filter((f) => (0, fileutils_1.fileExists)(f) && supportedExtensions.has(path.extname(f)));
// exclude patterns in .nxignore or .gitignore
const nonIgnoredPatterns = (0, ignore_1.getIgnoreObject)().filter(patterns);
if (args.libsAndApps) {
return getPatternsFromApps(nonIgnoredPatterns);
}
return nonIgnoredPatterns;
}
catch (err) {
output_1.output.error({
title: err?.message ||
'Something went wrong when resolving the list of files for the formatter',
bodyLines: [`Defaulting to all files pattern: "${allFilesPattern}"`],
});
return allFilesPattern;
}
}
async function getPatternsFromApps(affectedFiles) {
const graph = await (0, project_graph_1.createProjectGraphAsync)({
exitOnError: true,
});
const affectedGraph = await (0, affected_project_graph_1.filterAffected)(graph, (0, file_utils_1.calculateFileChanges)(affectedFiles));
return getPatternsFromProjects(Object.keys(affectedGraph.nodes), affectedGraph);
}
function addRootConfigFiles(chunkList, nxArgs) {
if (nxArgs.all) {
return;
}
const chunk = [];
const addToChunkIfNeeded = (file) => {
if (chunkList.every((c) => !c.includes(`"${file}"`))) {
chunk.push(file);
}
};
// if (workspaceJsonPath) {
// addToChunkIfNeeded(workspaceJsonPath);
// }
['nx.json', (0, typescript_1.getRootTsConfigFileName)()]
.filter(Boolean)
.forEach(addToChunkIfNeeded);
if (chunk.length > 0) {
chunkList.push(chunk);
}
}
function getPatternsFromProjects(projects, projectGraph) {
return (0, command_line_utils_1.getProjectRoots)(projects, projectGraph);
}
function write(prettier, patterns) {
if (patterns.length > 0) {
const [swcrcPatterns, regularPatterns] = patterns.reduce((result, pattern) => {
result[pattern.includes('.swcrc') ? 0 : 1].push(pattern);
return result;
}, [[], []]);
const prettierPath = getPrettierPath();
const listDifferentArg = shouldUseListDifferent(prettier.version)
? '--list-different '
: '';
(0, node_child_process_1.execSync)(`node "${prettierPath}" --write ${listDifferentArg}${regularPatterns.join(' ')}`, {
stdio: [0, 1, 2],
windowsHide: true,
});
if (swcrcPatterns.length > 0) {
(0, node_child_process_1.execSync)(`node "${prettierPath}" --write ${listDifferentArg}${swcrcPatterns.join(' ')} --parser json`, {
stdio: [0, 1, 2],
windowsHide: true,
});
}
}
}
async function check(patterns) {
if (patterns.length === 0) {
return [];
}
const prettierPath = getPrettierPath();
return new Promise((resolve, reject) => {
(0, node_child_process_1.exec)(`node "${prettierPath}" --list-different ${patterns.join(' ')}`, { encoding: 'utf-8', windowsHide: true }, (error, stdout) => {
if (error) {
// The command failed because Prettier threw an error.
if (stdout.length === 0) {
reject(error);
}
// The command failed so there are files with different formatting. Prettier writes them to stdout, newline separated.
resolve(stdout.trim().split('\n'));
}
else {
// The command succeeded so there are no files with different formatting
resolve([]);
}
});
});
}
function sortTsConfig() {
try {
const tsconfigPath = (0, typescript_1.getRootTsConfigPath)();
const tsconfig = (0, fileutils_1.readJsonFile)(tsconfigPath);
const sortedPaths = (0, object_sort_1.sortObjectByKeys)(tsconfig.compilerOptions.paths);
tsconfig.compilerOptions.paths = sortedPaths;
(0, fileutils_1.writeJsonFile)(tsconfigPath, tsconfig);
}
catch (e) {
// catch noop
}
}
let prettierPath;
function getPrettierPath() {
if (prettierPath) {
return prettierPath;
}
const { packageJson, path: packageJsonPath } = (0, package_json_1.readModulePackageJson)('prettier');
const bin = packageJson.bin;
const binPath = typeof bin === 'string' ? bin : bin?.['prettier'];
if (!binPath) {
throw new Error(`Could not find prettier binary in ${packageJsonPath}`);
}
prettierPath = path.resolve(path.dirname(packageJsonPath), binPath);
return prettierPath;
}
let useListDifferent;
/**
* Determines if --list-different should be used with --write.
* Prettier 4+ and 3.6.x with experimental CLI don't support combining these flags.
*/
function shouldUseListDifferent(prettierVersion) {
if (useListDifferent !== undefined) {
return useListDifferent;
}
const prettierMajor = (0, semver_1.major)(prettierVersion);
const isExperimentalCli = process.env.PRETTIER_EXPERIMENTAL_CLI === '1';
useListDifferent = prettierMajor < 4 && !isExperimentalCli;
return useListDifferent;
}

View File

@@ -0,0 +1,2 @@
import { CommandModule } from 'yargs';
export declare const yargsGenerateCommand: CommandModule;

View File

@@ -0,0 +1,67 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsGenerateCommand = void 0;
const shared_options_1 = require("../yargs-utils/shared-options");
const handle_import_1 = require("../../utils/handle-import");
exports.yargsGenerateCommand = {
command: 'generate <generator> [_..]',
describe: 'Generate or update source code (e.g., nx generate @nx/js:lib mylib).',
aliases: ['g'],
builder: (yargs) => withGenerateOptions(yargs),
handler: async (args) => {
// Remove the command from the args
args._ = args._.slice(1);
process.exit(await (await (0, handle_import_1.handleImport)('./generate.js', __dirname)).generate(args));
},
};
function withGenerateOptions(yargs) {
const generatorWillShowHelp = process.argv[3] && !process.argv[3].startsWith('-');
const res = (0, shared_options_1.withVerbose)(yargs)
.positional('generator', {
describe: 'Name of the generator (e.g., @nx/js:library, library).',
type: 'string',
required: true,
})
.option('dryRun', {
describe: 'Preview the changes without updating files.',
alias: 'd',
type: 'boolean',
default: false,
})
.option('interactive', {
describe: 'When false disables interactive input prompts for options.',
type: 'boolean',
default: true,
})
.option('quiet', {
describe: 'Hides logs from tree operations (e.g. `CREATE package.json`).',
type: 'boolean',
conflicts: ['verbose'],
})
.middleware((args) => {
if (process.env.NX_INTERACTIVE === 'false') {
args.interactive = false;
}
else {
process.env.NX_INTERACTIVE = `${args.interactive}`;
}
if (process.env.NX_DRY_RUN === 'true') {
args.dryRun = true;
}
else {
process.env.NX_DRY_RUN = `${args.dryRun}`;
}
if (process.env.NX_GENERATE_QUIET === 'true') {
args.quiet = true;
}
else {
process.env.NX_GENERATE_QUIET = `${args.quiet}`;
}
});
if (generatorWillShowHelp) {
return res.help(false);
}
else {
return res.epilog(`Run "nx g collection:generator --help" to see information about the generator's schema.`);
}
}

View File

@@ -0,0 +1,21 @@
import { FileChange } from '../../generators/tree';
import { Options, Schema } from '../../utils/params';
export interface GenerateOptions {
collectionName: string;
generatorName: string;
generatorOptions: Options;
help: boolean;
dryRun: boolean;
interactive: boolean;
defaults: boolean;
quiet: boolean;
}
export declare function printChanges(fileChanges: FileChange[]): void;
export declare function parseGeneratorString(value: string): {
collection?: string;
generator: string;
};
export declare function printGenHelp(opts: GenerateOptions, schema: Schema, normalizedGeneratorName: string, aliases: string[]): void;
export declare function generate(args: {
[k: string]: any;
}): Promise<number>;

View File

@@ -0,0 +1,281 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.printChanges = printChanges;
exports.parseGeneratorString = parseGeneratorString;
exports.printGenHelp = printGenHelp;
exports.generate = generate;
const tslib_1 = require("tslib");
const pc = tslib_1.__importStar(require("picocolors"));
const enquirer_1 = require("enquirer");
const path_1 = require("path");
const configuration_1 = require("../../config/configuration");
const tree_1 = require("../../generators/tree");
const project_graph_1 = require("../../project-graph/project-graph");
const retrieve_workspace_files_1 = require("../../project-graph/utils/retrieve-workspace-files");
const logger_1 = require("../../utils/logger");
const params_1 = require("../../utils/params");
const handle_errors_1 = require("../../utils/handle-errors");
const handle_import_1 = require("../../utils/handle-import");
const local_plugins_1 = require("../../utils/plugins/local-plugins");
const print_help_1 = require("../../utils/print-help");
const workspace_root_1 = require("../../utils/workspace-root");
const calculate_default_project_name_1 = require("../../config/calculate-default-project-name");
const installed_plugins_1 = require("../../utils/plugins/installed-plugins");
const generator_utils_1 = require("./generator-utils");
const path_2 = require("../../utils/path");
const analytics_1 = require("../../analytics");
function printChanges(fileChanges) {
fileChanges.forEach((f) => {
if (f.type === 'CREATE') {
console.log(`${pc.green('CREATE')} ${f.path}`);
}
else if (f.type === 'UPDATE') {
console.log(`${pc.white('UPDATE')} ${f.path}`);
}
else if (f.type === 'DELETE') {
console.log(`${pc.yellow('DELETE')} ${f.path}`);
}
});
}
async function promptForCollection(generatorName, interactive, projectsConfiguration) {
const localPlugins = await (0, local_plugins_1.getLocalWorkspacePlugins)(projectsConfiguration, (0, configuration_1.readNxJson)());
const installedCollections = Array.from(new Set((0, installed_plugins_1.findInstalledPlugins)().map((x) => x.name)));
const choicesMap = new Set();
const deprecatedChoices = new Set();
for (const collectionName of installedCollections) {
try {
const { resolvedCollectionName, normalizedGeneratorName, generatorConfiguration: { ['x-deprecated']: deprecated, hidden }, } = (0, generator_utils_1.getGeneratorInformation)(collectionName, generatorName, workspace_root_1.workspaceRoot, projectsConfiguration.projects);
if (hidden) {
continue;
}
if (deprecated) {
deprecatedChoices.add(`${resolvedCollectionName}:${normalizedGeneratorName}`);
}
else {
choicesMap.add(`${resolvedCollectionName}:${normalizedGeneratorName}`);
}
}
catch { }
}
const choicesFromLocalPlugins = [];
for (const [name] of localPlugins) {
try {
const { resolvedCollectionName, normalizedGeneratorName, generatorConfiguration: { ['x-deprecated']: deprecated, hidden }, } = (0, generator_utils_1.getGeneratorInformation)(name, generatorName, workspace_root_1.workspaceRoot, projectsConfiguration.projects);
if (hidden) {
continue;
}
const value = `${resolvedCollectionName}:${normalizedGeneratorName}`;
if (!choicesMap.has(value)) {
if (deprecated) {
deprecatedChoices.add(value);
}
else {
choicesFromLocalPlugins.push({
name: value,
message: pc.bold(value),
value,
});
}
}
}
catch { }
}
if (choicesFromLocalPlugins.length) {
choicesFromLocalPlugins[choicesFromLocalPlugins.length - 1].message += '\n';
}
const choices = choicesFromLocalPlugins.concat(...choicesMap);
if (choices.length === 1) {
return typeof choices[0] === 'string' ? choices[0] : choices[0].value;
}
else if (!interactive && choices.length > 1) {
throwInvalidInvocation(Array.from(choicesMap));
}
else if (interactive && choices.length > 1) {
const noneOfTheAbove = `\nNone of the above`;
choices.push(noneOfTheAbove);
let { generator, customCollection } = await (0, enquirer_1.prompt)([
{
name: 'generator',
message: `Which generator would you like to use?`,
type: 'autocomplete',
// enquirer's typings are incorrect here... It supports (string | Choice)[], but is typed as (string[] | Choice[])
choices: choices,
},
{
name: 'customCollection',
type: 'input',
message: `Which collection would you like to use?`,
skip: function () {
// Skip this question if the user did not answer None of the above
return this.state.answers.generator !== noneOfTheAbove;
},
validate: function (value) {
if (this.skipped) {
return true;
}
try {
(0, generator_utils_1.getGeneratorInformation)(value, generatorName, workspace_root_1.workspaceRoot, projectsConfiguration.projects);
return true;
}
catch {
logger_1.logger.error(`\nCould not find ${value}:${generatorName}`);
return false;
}
},
},
]);
return customCollection
? `${customCollection}:${generatorName}`
: generator;
}
else if (deprecatedChoices.size > 0) {
throw new Error([
`All installed generators named "${generatorName}" are deprecated. To run one, provide its full \`collection:generator\` id.`,
[...deprecatedChoices].map((x) => ` - ${x}`),
].join('\n'));
}
else {
throw new Error(`Could not find any generators named "${generatorName}"`);
}
}
function parseGeneratorString(value) {
const separatorIndex = value.lastIndexOf(':');
if (separatorIndex > 0) {
return {
collection: value.slice(0, separatorIndex),
generator: value.slice(separatorIndex + 1),
};
}
else {
return {
generator: value,
};
}
}
async function convertToGenerateOptions(generatorOptions, mode, projectsConfiguration) {
let collectionName = null;
let generatorName = null;
const interactive = generatorOptions.interactive;
if (mode === 'generate') {
const generatorDescriptor = generatorOptions['generator'];
const { collection, generator } = parseGeneratorString(generatorDescriptor);
if (collection) {
collectionName = collection;
generatorName = generator;
}
else {
const generatorString = await promptForCollection(generatorDescriptor, interactive, projectsConfiguration);
const parsedGeneratorString = parseGeneratorString(generatorString);
collectionName = parsedGeneratorString.collection;
generatorName = parsedGeneratorString.generator;
}
}
else {
collectionName = generatorOptions.collection;
generatorName = 'new';
}
const res = {
collectionName,
generatorName,
generatorOptions,
help: generatorOptions.help,
dryRun: generatorOptions.dryRun,
interactive,
defaults: generatorOptions.defaults,
quiet: generatorOptions.quiet,
};
delete generatorOptions.d;
delete generatorOptions.dryRun;
delete generatorOptions['dry-run'];
delete generatorOptions.interactive;
delete generatorOptions.help;
delete generatorOptions.collection;
delete generatorOptions.verbose;
delete generatorOptions.generator;
delete generatorOptions['--'];
delete generatorOptions['$0'];
delete generatorOptions.quiet;
return res;
}
function throwInvalidInvocation(availableGenerators) {
throw new Error(`Specify the generator name (e.g., nx generate ${availableGenerators.join(', ')})`);
}
function printGenHelp(opts, schema, normalizedGeneratorName, aliases) {
(0, print_help_1.printHelp)(`generate ${opts.collectionName}:${normalizedGeneratorName}`, {
...schema,
properties: schema.properties,
}, {
mode: 'generate',
plugin: opts.collectionName,
entity: normalizedGeneratorName,
aliases,
});
}
async function generate(args) {
return (0, handle_errors_1.handleErrors)(args.verbose, async () => {
const nxJsonConfiguration = (0, configuration_1.readNxJson)();
let projectGraph;
let projectsConfigurations;
if (args.skipProjectGraph) {
const projects = await (0, retrieve_workspace_files_1.retrieveProjectConfigurationsWithoutPluginInference)(workspace_root_1.workspaceRoot);
projectsConfigurations = { version: 2, projects };
}
else {
projectGraph = await (0, project_graph_1.createProjectGraphAsync)();
projectsConfigurations =
(0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
}
const opts = await convertToGenerateOptions(args, 'generate', projectsConfigurations);
const { normalizedGeneratorName, schema, implementationFactory, generatorConfiguration: { aliases, hidden, ['x-deprecated']: deprecated, ['x-use-standalone-layout']: isStandalonePreset, }, } = (0, generator_utils_1.getGeneratorInformation)(opts.collectionName, opts.generatorName, workspace_root_1.workspaceRoot, projectsConfigurations.projects);
if (deprecated) {
logger_1.logger.warn([
`${logger_1.NX_PREFIX}: ${opts.collectionName}:${normalizedGeneratorName} is deprecated`,
`${deprecated}`,
].join('\n'));
}
if (!opts.quiet && !opts.help) {
logger_1.logger.info(`NX Generating ${opts.collectionName}:${normalizedGeneratorName}`);
}
if (opts.help) {
printGenHelp(opts, schema, normalizedGeneratorName, aliases);
return 0;
}
const cwd = (0, path_2.getCwd)();
const combinedOpts = await (0, params_1.combineOptionsForGenerator)(opts.generatorOptions, opts.collectionName, normalizedGeneratorName, projectsConfigurations, nxJsonConfiguration, schema, opts.interactive, (0, calculate_default_project_name_1.calculateDefaultProjectName)(cwd, workspace_root_1.workspaceRoot, projectsConfigurations, nxJsonConfiguration), (0, path_1.relative)(workspace_root_1.workspaceRoot, cwd), args.verbose);
(0, analytics_1.reportNxGenerateCommand)(`${opts.collectionName}:${normalizedGeneratorName}`);
if ((0, generator_utils_1.getGeneratorInformation)(opts.collectionName, normalizedGeneratorName, workspace_root_1.workspaceRoot, projectsConfigurations.projects).isNxGenerator) {
const host = new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose, `generating (${opts.collectionName}:${normalizedGeneratorName})`);
const implementation = implementationFactory();
// @todo(v17): Remove this, isStandalonePreset property is defunct.
if (normalizedGeneratorName === 'preset' && !isStandalonePreset) {
host.write('apps/.gitkeep', '');
host.write('libs/.gitkeep', '');
}
const task = await implementation(host, combinedOpts);
host.lock();
const changes = host.listChanges();
if (!opts.quiet) {
printChanges(changes);
}
if (!opts.dryRun) {
(0, tree_1.flushChanges)(workspace_root_1.workspaceRoot, changes);
if (task) {
await task();
}
}
else {
logger_1.logger.warn(`\nNOTE: The "dryRun" flag means no changes were made.`);
}
}
else {
if (!projectGraph) {
throw new Error(`Cannot run non-Nx generators with --skipProjectGraph. Remove the flag or use an Nx generator.`);
}
require('../../adapter/compat');
return (await (0, handle_import_1.handleImport)('../../adapter/ngcli-adapter.js', __dirname)).generate(workspace_root_1.workspaceRoot, {
...opts,
generatorOptions: combinedOpts,
}, projectsConfigurations.projects, args.verbose, projectGraph);
}
});
}

View File

@@ -0,0 +1,18 @@
import { Generator, GeneratorsJson, GeneratorsJsonEntry } from '../../config/misc-interfaces';
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
export type GeneratorInformation = {
resolvedCollectionName: string;
normalizedGeneratorName: string;
schema: any;
implementationFactory: () => Generator<unknown>;
isNgCompat: boolean;
isNxGenerator: boolean;
generatorConfiguration: GeneratorsJsonEntry;
};
export declare function getGeneratorInformation(collectionName: string, generatorName: string, root: string | null, projects: Record<string, ProjectConfiguration>): GeneratorInformation;
export declare function readGeneratorsJson(collectionName: string, generator: string, root: string | null, projects: Record<string, ProjectConfiguration>): {
generatorsFilePath: string;
generatorsJson: GeneratorsJson;
normalizedGeneratorName: string;
resolvedCollectionName: string;
};

View File

@@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGeneratorInformation = getGeneratorInformation;
exports.readGeneratorsJson = readGeneratorsJson;
const path_1 = require("path");
const schema_utils_1 = require("../../config/schema-utils");
const fileutils_1 = require("../../utils/fileutils");
const plugins_1 = require("../../project-graph/plugins");
const installation_directory_1 = require("../../utils/installation-directory");
function getGeneratorInformation(collectionName, generatorName, root, projects) {
try {
const { generatorsFilePath, generatorsJson, resolvedCollectionName, normalizedGeneratorName, } = readGeneratorsJson(collectionName, generatorName, root, projects);
const generatorsDir = (0, path_1.dirname)(generatorsFilePath);
const generatorConfig = generatorsJson.generators?.[normalizedGeneratorName] ||
generatorsJson.schematics?.[normalizedGeneratorName];
const isNgCompat = !generatorsJson.generators?.[normalizedGeneratorName];
const schemaPath = (0, schema_utils_1.resolveSchema)(generatorConfig.schema, generatorsDir, collectionName, projects);
const schema = (0, fileutils_1.readJsonFile)(schemaPath);
if (!schema.properties || typeof schema.properties !== 'object') {
schema.properties = {};
}
generatorConfig.implementation =
generatorConfig.implementation || generatorConfig.factory;
const implementationFactory = (0, schema_utils_1.getImplementationFactory)(generatorConfig.implementation, generatorsDir, collectionName, projects);
const normalizedGeneratorConfiguration = {
...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${process.env.NX_VERBOSE_LOGGING === 'true' ? e.stack : e.message}`);
}
}
function readGeneratorsJson(collectionName, generator, root, projects) {
let generatorsFilePath;
if (collectionName.endsWith('.json')) {
generatorsFilePath = require.resolve(collectionName, {
paths: root
? [...(0, installation_directory_1.getNxRequirePaths)(root), __dirname]
: [...(0, installation_directory_1.getNxRequirePaths)(), __dirname],
});
}
else {
const { json: packageJson, path: packageJsonPath } = (0, plugins_1.readPluginPackageJson)(collectionName, projects, root
? [...(0, installation_directory_1.getNxRequirePaths)(root), __dirname]
: [...(0, installation_directory_1.getNxRequirePaths)(), __dirname]);
const generatorsFile = packageJson.generators ?? packageJson.schematics;
if (!generatorsFile) {
throw new Error(`The "${collectionName}" package does not support Nx generators.`);
}
generatorsFilePath = require.resolve((0, path_1.join)((0, path_1.dirname)(packageJsonPath), generatorsFile));
}
const generatorsJson = (0, fileutils_1.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, projects);
}
catch (e) { }
}
throw new Error(`Cannot find generator '${generator}' in ${generatorsFilePath}.`);
}
return {
generatorsFilePath,
generatorsJson,
normalizedGeneratorName,
resolvedCollectionName: collectionName,
};
}
function findFullGeneratorName(name, generators) {
if (generators) {
for (let [key, data] of Object.entries(generators)) {
if (key === name ||
(data.aliases && data.aliases.includes(name))) {
return key;
}
}
}
}

View File

@@ -0,0 +1,25 @@
import { Argv, CommandModule } from 'yargs';
export declare const yargsGraphCommand: CommandModule;
export declare function withGraphOptions(yargs: Argv): Argv<{
file: string;
} & {
print: boolean;
} & {
view: string;
} & {
targets: string;
} & {
focus: string;
} & {
exclude: string;
} & {
groupByFolder: boolean;
} & {
host: string;
} & {
port: number;
} & {
watch: boolean;
} & {
open: boolean;
}>;

View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsGraphCommand = void 0;
exports.withGraphOptions = withGraphOptions;
const documentation_1 = require("../yargs-utils/documentation");
const shared_options_1 = require("../yargs-utils/shared-options");
const handle_import_1 = require("../../utils/handle-import");
exports.yargsGraphCommand = {
command: 'graph',
describe: 'Graph dependencies within workspace.',
aliases: ['dep-graph'],
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withVerbose)((0, shared_options_1.withAffectedOptions)(withGraphOptions(yargs))), 'dep-graph')
.option('affected', {
type: 'boolean',
description: 'Highlight affected projects.',
})
.implies('untracked', 'affected')
.implies('uncommitted', 'affected')
.implies('files', 'affected')
.implies('base', 'affected')
.implies('head', 'affected'),
handler: async (args) => await (await (0, handle_import_1.handleImport)('./graph.js', __dirname)).generateGraph(args, []),
};
function withGraphOptions(yargs) {
return yargs
.option('file', {
describe: 'Output file (e.g. --file=output.json or --file=dep-graph.html).',
type: 'string',
})
.option('print', {
describe: 'Print the project graph to stdout in the terminal.',
type: 'boolean',
})
.option('view', {
describe: 'Choose whether to view the projects or task graph.',
type: 'string',
default: 'projects',
choices: ['projects', 'tasks'],
})
.option('targets', {
describe: 'The target to show tasks for in the task graph.',
type: 'string',
coerce: shared_options_1.parseCSV,
})
.option('focus', {
describe: 'Use to show the project graph for a particular project and every node that is either an ancestor or a descendant.',
type: 'string',
})
.option('exclude', {
describe: 'List of projects delimited by commas to exclude from the project graph.',
type: 'string',
coerce: shared_options_1.parseCSV,
})
.option('groupByFolder', {
describe: 'Group projects by folder in the project graph.',
type: 'boolean',
})
.option('host', {
describe: 'Bind the project graph server to a specific ip address.',
type: 'string',
})
.option('port', {
describe: 'Bind the project graph server to a specific port.',
type: 'number',
})
.option('watch', {
describe: 'Watch for changes to project graph and update in-browser.',
type: 'boolean',
default: true,
})
.option('open', {
describe: 'Open the project graph in the browser.',
type: 'boolean',
default: true,
});
}

69
node_modules/nx/dist/src/command-line/graph/graph.d.ts generated vendored Normal file
View File

@@ -0,0 +1,69 @@
import { ProjectFileMap, ProjectGraph, ProjectGraphDependency, ProjectGraphProjectNode } from '../../config/project-graph';
import { TaskGraph } from '../../config/task-graph';
export interface GraphError {
message: string;
stack: string;
cause: unknown;
name: string;
pluginName: string;
fileName?: string;
}
export interface ProjectGraphClientResponse {
hash: string;
projects: ProjectGraphProjectNode[];
dependencies: Record<string, ProjectGraphDependency[]>;
fileMap?: ProjectFileMap;
layout: {
appsDir: string;
libsDir: string;
};
affected: string[];
focus: string;
groupByFolder: boolean;
exclude: string[];
isPartial: boolean;
errors?: GraphError[];
connectedToCloud?: boolean;
disabledTaskSyncGenerators?: string[];
}
export interface TaskGraphClientResponse {
taskGraph: TaskGraph;
plans?: Record<string, string[]>;
error?: string | null;
}
export interface ExpandedTaskInputsReponse {
[taskId: string]: Record<string, string[]>;
}
export declare function generateGraph(args: {
file?: string;
print?: boolean;
host?: string;
port?: number;
groupByFolder?: boolean;
watch?: boolean;
open?: boolean;
view: 'projects' | 'tasks' | 'project-details';
projects?: string[];
all?: boolean;
targets?: string[];
focus?: string;
exclude?: string[];
affected?: boolean;
}, affectedProjects: string[]): Promise<void>;
/**
* The data type that `nx graph --file graph.json` or `nx build --graph graph.json` contains
*/
export interface GraphJson {
/**
* A graph of tasks populated with `nx build --graph`
*/
tasks?: TaskGraph;
/**
* The plans for hashing a task in the task graph
*/
taskPlans?: Record<string, string[]>;
/**
* The project graph
*/
graph: ProjectGraph;
}

978
node_modules/nx/dist/src/command-line/graph/graph.js generated vendored Normal file
View File

@@ -0,0 +1,978 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateGraph = generateGraph;
const tslib_1 = require("tslib");
const crypto_1 = require("crypto");
const node_child_process_1 = require("node:child_process");
const node_fs_1 = require("node:fs");
const daemon_socket_messenger_1 = require("../../daemon/client/daemon-socket-messenger");
const http = tslib_1.__importStar(require("node:http"));
const minimatch_1 = require("minimatch");
const node_url_1 = require("node:url");
const open_1 = tslib_1.__importDefault(require("open"));
const node_path_1 = require("node:path");
const net = tslib_1.__importStar(require("node:net"));
const node_perf_hooks_1 = require("node:perf_hooks");
const configuration_1 = require("../../config/configuration");
const fileutils_1 = require("../../utils/fileutils");
const output_1 = require("../../utils/output");
const workspace_root_1 = require("../../utils/workspace-root");
const client_1 = require("../../daemon/client/client");
const typescript_1 = require("../../plugins/js/utils/typescript");
const operators_1 = require("../../project-graph/operators");
const project_graph_1 = require("../../project-graph/project-graph");
const create_task_graph_1 = require("../../tasks-runner/create-task-graph");
const all_file_data_1 = require("../../utils/all-file-data");
const command_line_utils_1 = require("../../utils/command-line-utils");
const native_1 = require("../../native");
const transform_objects_1 = require("../../native/transform-objects");
const affected_1 = require("../affected/affected");
const nx_deps_cache_1 = require("../../project-graph/nx-deps-cache");
const task_hasher_1 = require("../../hasher/task-hasher");
const find_matching_projects_1 = require("../../utils/find-matching-projects");
const create_task_hasher_1 = require("../../hasher/create-task-hasher");
const task_env_1 = require("../../tasks-runner/task-env");
const error_types_1 = require("../../project-graph/error-types");
const nx_cloud_utils_1 = require("../../utils/nx-cloud-utils");
// maps file extention to MIME types
const mimeType = {
'.ico': 'image/x-icon',
'.html': 'text/html',
'.js': 'text/javascript',
'.json': 'application/json',
'.css': 'text/css',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.wav': 'audio/wav',
'.mp3': 'audio/mpeg',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.eot': 'appliaction/vnd.ms-fontobject',
'.ttf': 'aplication/font-sfnt',
};
function buildEnvironmentJs(exclude, watchMode, localMode, depGraphClientResponse, taskGraphClientResponse, expandedTaskInputsReponse, sourceMapsResponse) {
let environmentJs = `window.exclude = ${JSON.stringify(exclude)};
window.watch = ${!!watchMode};
window.environment = 'release';
window.localMode = '${localMode}';
window.appConfig = {
showDebugger: false,
showExperimentalFeatures: false,
workspaces: [
{
id: 'local',
label: 'local',
projectGraphUrl: 'project-graph.json',
taskGraphUrl: 'task-graph.json',
taskInputsUrl: 'task-inputs.json',
sourceMapsUrl: 'source-maps.json'
}
],
defaultWorkspaceId: 'local',
};
`;
if (localMode === 'build') {
environmentJs += `window.projectGraphResponse = ${JSON.stringify(depGraphClientResponse)};
`;
environmentJs += `window.taskGraphResponse = ${JSON.stringify(taskGraphClientResponse)};
`;
environmentJs += `window.expandedTaskInputsResponse = ${JSON.stringify(expandedTaskInputsReponse)};`;
environmentJs += `window.sourceMapsResponse = ${JSON.stringify(sourceMapsResponse)};`;
}
else {
environmentJs += `window.projectGraphResponse = null;`;
environmentJs += `window.taskGraphResponse = null;`;
environmentJs += `window.expandedTaskInputsResponse = null;`;
environmentJs += `window.sourceMapsResponse = null;`;
}
return environmentJs;
}
function projectExists(projects, projectToFind) {
return (projects.find((project) => project.name === projectToFind) !== undefined);
}
function hasPath(graph, target, node, visited) {
if (target === node)
return true;
for (const d of graph.dependencies[node] || []) {
if (visited.has(d.target))
continue;
visited.add(d.target);
if (hasPath(graph, target, d.target, visited))
return true;
}
return false;
}
function filterGraph(graph, focus, exclude) {
let projectNames = Object.values(graph.nodes).map((project) => project.name);
let filteredProjectNames;
if (focus !== null) {
filteredProjectNames = new Set();
projectNames.forEach((p) => {
const isInPath = hasPath(graph, p, focus, new Set()) ||
hasPath(graph, focus, p, new Set());
if (isInPath) {
filteredProjectNames.add(p);
}
});
}
else {
filteredProjectNames = new Set(projectNames);
}
if (exclude.length !== 0) {
exclude.forEach((p) => filteredProjectNames.delete(p));
}
let filteredGraph = {
nodes: {},
dependencies: {},
};
filteredProjectNames.forEach((p) => {
filteredGraph.nodes[p] = graph.nodes[p];
filteredGraph.dependencies[p] = graph.dependencies[p];
});
return filteredGraph;
}
async function generateGraph(args, affectedProjects) {
if (args.view === 'project-details' && !args.focus) {
output_1.output.error({
title: `The project details view requires the --focus option.`,
});
process.exit(1);
}
if (args.view === 'project-details' && (args.targets || args.affected)) {
output_1.output.error({
title: `The project details view can only be used with the --focus option.`,
bodyLines: [
`You passed ${args.targets ? '--targets ' : ''}${args.affected ? '--affected ' : ''}`,
],
});
process.exit(1);
}
let rawGraph;
let sourceMaps;
try {
const projectGraphAndSourceMaps = await (0, project_graph_1.createProjectGraphAndSourceMapsAsync)({ exitOnError: false });
rawGraph = projectGraphAndSourceMaps.projectGraph;
sourceMaps = projectGraphAndSourceMaps.sourceMaps;
}
catch (e) {
if (e instanceof error_types_1.ProjectGraphError) {
rawGraph = e.getPartialProjectGraph();
sourceMaps = e.getPartialSourcemaps();
}
if (!rawGraph) {
(0, project_graph_1.handleProjectGraphError)({ exitOnError: true }, e);
}
else {
const errors = e.getErrors();
if (errors?.length > 0) {
errors.forEach((e) => {
output_1.output.error({ title: e.message, bodyLines: [e.stack] });
});
}
output_1.output.warn({
title: `${errors?.length > 1 ? `${errors.length} errors` : `An error`} occured while processing the project graph. Showing partial graph.`,
});
}
}
let prunedGraph = (0, operators_1.pruneExternalNodes)(rawGraph);
const projects = Object.values(prunedGraph.nodes);
projects.sort((a, b) => {
return a.name.localeCompare(b.name);
});
if (args.focus) {
if (!projectExists(projects, args.focus)) {
output_1.output.error({
title: `Project to focus does not exist.`,
bodyLines: [`You provided --focus=${args.focus}`],
});
process.exit(1);
}
}
try {
affectedProjects = (await (0, affected_1.getAffectedGraphNodes)((0, command_line_utils_1.splitArgsIntoNxArgsAndOverrides)(args, 'affected', {
printWarnings: args.affected && !args.print && args.file !== 'stdout',
}, (0, configuration_1.readNxJson)()).nxArgs, rawGraph)).map((n) => n.name);
}
catch (e) {
// if `--affected` is explicitly passed in or
// resolved `args.affected` is true, then calculating affected projects
// is intended (and expected) so we rethrow the error here.
if (args.affected) {
throw e;
}
// if `affected` is falsy, and we calculate affected projects for default case
// and the operation might fail (i.e: in e2e tests), we fallback to empty array
affectedProjects = [];
}
let excludePatterns = [];
if (args.exclude && args.exclude.length > 0) {
try {
// Use findMatchingProjects to expand patterns (supports globs, tags, directories, etc.)
excludePatterns = (0, find_matching_projects_1.findMatchingProjects)(args.exclude, prunedGraph.nodes);
// If no projects matched any of the exclude patterns, show a warning
if (excludePatterns.length === 0) {
output_1.output.warn({
title: `No projects matched the following exclude patterns:`,
bodyLines: args.exclude,
});
}
}
catch (e) {
output_1.output.error({
title: `Invalid exclude pattern:`,
bodyLines: [e.message],
});
process.exit(1);
}
}
let html = (0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '../../core/graph/index.html'), 'utf-8');
prunedGraph = filterGraph(prunedGraph, args.focus || null, excludePatterns);
if (args.print || args.file === 'stdout') {
console.log(JSON.stringify(await createJsonOutput(prunedGraph, rawGraph, args.projects, args.targets), null, 2));
await output_1.output.drain();
await new Promise((res) => setImmediate(res));
process.exit(0);
}
if (args.file) {
const workspaceFolder = workspace_root_1.workspaceRoot;
const ext = (0, node_path_1.extname)(args.file);
const fullFilePath = (0, node_path_1.isAbsolute)(args.file)
? args.file
: (0, node_path_1.join)(workspaceFolder, args.file);
const fileFolderPath = (0, node_path_1.dirname)(fullFilePath);
if (ext === '.html') {
const assetsFolder = (0, node_path_1.join)(fileFolderPath, 'static');
const assets = [];
(0, node_fs_1.cpSync)((0, node_path_1.join)(__dirname, '../../core/graph'), assetsFolder, {
filter: (_src, dest) => {
const isntHtml = !/index\.html/.test(dest);
if (isntHtml && dest.includes('.')) {
assets.push(dest);
}
return isntHtml;
},
recursive: true,
});
const { projectGraphClientResponse } = await createProjectGraphAndSourceMapClientResponse(affectedProjects);
const taskGraphClientResponse = args.targets
? await createTaskGraphForTargetsAndProjects(args.targets, args.projects)
: await createTaskGraphClientResponse();
const taskInputsReponse = await createExpandedTaskInputResponse(taskGraphClientResponse, projectGraphClientResponse);
const environmentJs = buildEnvironmentJs(excludePatterns, args.watch, !!args.file && args.file.endsWith('html') ? 'build' : 'serve', projectGraphClientResponse, taskGraphClientResponse, taskInputsReponse, sourceMaps);
html = html.replace(/src="/g, 'src="static/');
html = html.replace(/href="styles/g, 'href="static/styles');
html = html.replace(/<base href="\/".*>/g, '');
html = html.replace(/type="module"/g, '');
(0, node_fs_1.writeFileSync)(fullFilePath, html);
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(assetsFolder, 'environment.js'), environmentJs);
output_1.output.success({
title: `HTML output created in ${fileFolderPath}`,
bodyLines: [fileFolderPath, ...assets],
});
}
else if (ext === '.json') {
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(fullFilePath), { recursive: true });
const json = await createJsonOutput(prunedGraph, rawGraph, args.projects, args.targets);
(0, fileutils_1.writeJsonFile)(fullFilePath, json);
output_1.output.success({
title: `JSON output created in ${fileFolderPath}`,
bodyLines: [fullFilePath],
});
}
else {
output_1.output.error({
title: `Please specify a filename with either .json or .html extension.`,
bodyLines: [`You provided --file=${args.file}`],
});
process.exit(1);
}
await new Promise((res) => setImmediate(res));
process.exit(0);
}
else {
const environmentJs = buildEnvironmentJs(excludePatterns, args.watch, !!args.file && args.file.endsWith('html') ? 'build' : 'serve');
let app;
let url;
try {
const result = await startServer(html, environmentJs, args.host || '127.0.0.1', args.port || 4211, args.watch, affectedProjects, args.focus, args.groupByFolder, excludePatterns);
app = result.app;
url = result.url;
}
catch (err) {
output_1.output.error({
title: 'Failed to start graph server',
bodyLines: [err.message],
});
process.exit(1);
}
// setting up `?graph=serialized-graph-state`
let graphState = undefined;
url.pathname = args.view;
if (args.focus) {
if (args.view === 'project-details') {
url.pathname += '/' + encodeURIComponent(args.focus);
}
else if (args.view === 'projects') {
graphState ??= { config: {} };
graphState.state = {
type: 'focused',
nodeId: encodeURIComponent(`project-${args.focus}`),
};
}
}
// Add targets as query parameters for tasks view
if (args.view === 'tasks' && args.targets && args.targets.length > 0) {
const targets = Array.isArray(args.targets)
? args.targets
: [args.targets];
url.searchParams.append('targets', targets.join(' '));
}
if (args.all) {
if (args.view === 'tasks') {
url.pathname += '/all';
}
}
else if (args.projects) {
url.searchParams.append('projects', args.projects.map((projectName) => projectName).join(' '));
}
else if (args.affected) {
graphState ??= { config: {} };
graphState.config = { ...graphState.config, showMode: 'affected' };
}
if (graphState && args.view === 'projects') {
// only projects graph restore-able state is relevant at the moment
url.searchParams.set('rawGraph', JSON.stringify(graphState));
}
output_1.output.success({
title: `Project graph started at ${url.toString()}`,
});
if (args.open) {
(0, open_1.default)(url.toString());
}
return new Promise((res) => {
app.once('close', res);
});
}
}
function findAvailablePort(startPort, host = '127.0.0.1') {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.listen(startPort, host, () => {
const port = server.address().port;
server.close(() => {
resolve(port);
});
});
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
// Port is in use, try the next one
findAvailablePort(startPort + 1, host)
.then(resolve)
.catch(reject);
}
else {
reject(err);
}
});
});
}
async function startServer(html, environmentJs, host, port = 4211, watchForChanges = true, affected = [], focus = null, groupByFolder = false, exclude = []) {
let unregisterFileWatcher;
if (watchForChanges && !client_1.daemonClient.enabled()) {
output_1.output.warn({
title: 'Nx Daemon is not enabled. Graph will not refresh on file changes.',
});
}
if (watchForChanges && client_1.daemonClient.enabled()) {
unregisterFileWatcher = await createProjectGraphListener();
}
const { projectGraphClientResponse, sourceMapResponse } = await createProjectGraphAndSourceMapClientResponse(affected, focus, exclude);
currentProjectGraphClientResponse = projectGraphClientResponse;
currentProjectGraphClientResponse.focus = focus;
currentProjectGraphClientResponse.groupByFolder = groupByFolder;
currentProjectGraphClientResponse.exclude = exclude;
currentSourceMapsClientResponse = sourceMapResponse;
isFilteredGraph = !!(focus || exclude.length > 0);
const app = http.createServer(async (req, res) => {
// parse URL
const parsedUrl = new node_url_1.URL(req.url, `http://${host}:${port}`);
// extract URL path
// Avoid https://en.wikipedia.org/wiki/Directory_traversal_attack
// e.g curl --path-as-is http://localhost:9000/../fileInDanger.txt
// by limiting the path to current directory only
const sanitizePath = (0, node_path_1.basename)(parsedUrl.pathname);
if (sanitizePath === 'project-graph.json') {
const requestFull = parsedUrl.searchParams.get('full') === 'true';
// If client requests full graph and current is filtered, regenerate
if (requestFull && isFilteredGraph) {
const { projectGraphClientResponse, sourceMapResponse } = await createProjectGraphAndSourceMapClientResponse([], null, []);
currentProjectGraphClientResponse = projectGraphClientResponse;
currentProjectGraphClientResponse.focus = null;
currentProjectGraphClientResponse.groupByFolder = false;
currentProjectGraphClientResponse.exclude = [];
currentSourceMapsClientResponse = sourceMapResponse;
isFilteredGraph = false;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(currentProjectGraphClientResponse));
return;
}
if (sanitizePath === 'task-graph.json') {
const projectsParam = parsedUrl.searchParams.get('projects');
const targetsParam = parsedUrl.searchParams.get('targets');
const configuration = parsedUrl.searchParams.get('configuration');
res.writeHead(200, { 'Content-Type': 'application/json' });
if (targetsParam) {
const targetNames = targetsParam.split(' ').filter(Boolean);
const projectNames = projectsParam
? projectsParam.split(' ').filter(Boolean)
: undefined;
return res.end(JSON.stringify(await createTaskGraphForTargetsAndProjects(targetNames, projectNames, configuration)));
}
// load all task graphs if there's no targets specified
return res.end(JSON.stringify(await createTaskGraphClientResponse()));
}
if (sanitizePath === 'task-inputs.json') {
node_perf_hooks_1.performance.mark('task input generation:start');
const taskId = parsedUrl.searchParams.get('taskId');
res.writeHead(200, { 'Content-Type': 'application/json' });
const inputs = await getExpandedTaskInputs(taskId);
node_perf_hooks_1.performance.mark('task input generation:end');
res.end(JSON.stringify({ [taskId]: inputs }));
node_perf_hooks_1.performance.measure('task input generation', 'task input generation:start', 'task input generation:end');
return;
}
if (sanitizePath === 'source-maps.json') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(currentSourceMapsClientResponse));
return;
}
if (sanitizePath === 'currentHash') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ hash: currentProjectGraphClientResponse.hash }));
return;
}
if (sanitizePath === 'environment.js') {
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(environmentJs);
return;
}
if (sanitizePath === 'help') {
const project = parsedUrl.searchParams.get('project');
const target = parsedUrl.searchParams.get('target');
try {
const text = getHelpTextFromTarget(project, target);
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(JSON.stringify({ text, success: true }));
}
catch (err) {
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(JSON.stringify({ text: err.message, success: false }));
}
return;
}
let pathname = (0, node_path_1.join)(__dirname, '../../core/graph/', sanitizePath);
// if the file is not found or is a directory, return index.html
if (!(0, node_fs_1.existsSync)(pathname) || (0, node_fs_1.statSync)(pathname).isDirectory()) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(html);
return;
}
try {
const data = (0, node_fs_1.readFileSync)(pathname);
const ext = (0, node_path_1.parse)(pathname).ext;
res.setHeader('Content-type', mimeType[ext] || 'text/plain');
res.end(data);
}
catch (err) {
res.statusCode = 500;
res.end(`Error getting the file: ${err}.`);
}
});
const handleTermination = async (exitCode) => {
if (unregisterFileWatcher) {
unregisterFileWatcher();
}
process.exit(exitCode);
};
process.on('SIGINT', () => handleTermination(128 + 2));
process.on('SIGTERM', () => handleTermination(128 + 15));
// Find an available port starting from the requested port
const availablePort = await findAvailablePort(port, host);
return new Promise((res, rej) => {
app.on('error', (err) => {
rej(err);
});
app.listen(availablePort, host, () => {
if (availablePort !== port) {
output_1.output.note({
title: `Port ${port} was already in use, using port ${availablePort} instead`,
});
}
res({ app, url: new node_url_1.URL(`http://${host}:${availablePort}`) });
});
});
}
let currentProjectGraphClientResponse = {
hash: null,
projects: [],
dependencies: {},
fileMap: {},
layout: {
appsDir: '',
libsDir: '',
},
affected: [],
focus: null,
groupByFolder: false,
exclude: [],
isPartial: false,
errors: [],
};
let currentSourceMapsClientResponse = {};
let isFilteredGraph = false;
function debounce(fn, time) {
let timeout;
return ((...args) => {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => fn(...args), time);
});
}
function createProjectGraphListener() {
return client_1.daemonClient.registerProjectGraphRecomputationListener(debounce(async (error, data) => {
if (error === 'reconnecting') {
output_1.output.note({ title: 'Daemon restarting, reconnecting...' });
return;
}
else if (error === 'reconnected') {
output_1.output.note({ title: 'Reconnected to daemon' });
return;
}
else if (error === 'closed') {
output_1.output.error({
title: `Failed to reconnect to daemon after multiple attempts`,
});
process.exit(1);
}
else if (error instanceof daemon_socket_messenger_1.VersionMismatchError) {
output_1.output.error({
title: 'Nx version changed. Please restart your command.',
});
process.exit(1);
}
else if (error) {
output_1.output.error({
title: `Watch error: ${error?.message ?? 'Unknown'}`,
});
}
else if (data !== null) {
output_1.output.note({ title: 'Project graph recomputed, updating...' });
let projectGraph = data.projectGraph;
let sourceMaps = data.sourceMaps;
let errors;
if (data.error instanceof error_types_1.ProjectGraphError) {
projectGraph = data.error.getPartialProjectGraph();
sourceMaps = data.error.getPartialSourcemaps();
errors = data.error.getErrors().map((e) => ({
message: e.message,
stack: e.stack,
cause: e.cause,
name: e.name,
pluginName: e.pluginName,
fileName: e.file ?? e.cause?.errors?.[0]?.location?.file,
}));
}
const { projectGraphClientResponse, sourceMapResponse } = transformProjectGraphToClientResponse(projectGraph, sourceMaps, errors, currentProjectGraphClientResponse.affected, isFilteredGraph ? currentProjectGraphClientResponse.focus : null, isFilteredGraph ? currentProjectGraphClientResponse.exclude : []);
if (projectGraphClientResponse.hash !==
currentProjectGraphClientResponse.hash &&
sourceMapResponse) {
if (projectGraphClientResponse.errors?.length > 0) {
projectGraphClientResponse.errors.forEach((e) => {
output_1.output.error({
title: e.message,
bodyLines: [e.stack],
});
});
output_1.output.warn({
title: `${projectGraphClientResponse.errors.length > 1
? `${projectGraphClientResponse.errors.length} errors`
: `An error`} occurred while processing the project graph. Showing partial graph.`,
});
}
output_1.output.note({ title: 'Graph changes updated.' });
currentProjectGraphClientResponse = projectGraphClientResponse;
currentSourceMapsClientResponse = sourceMapResponse;
// Clear task graph cache when project graph changes
clearTaskGraphCache();
}
else {
output_1.output.note({ title: 'No graph changes found.' });
}
}
}, 500));
}
function transformProjectGraphToClientResponse(projectGraph, sourceMaps, errors, affected = [], focus = null, exclude = []) {
node_perf_hooks_1.performance.mark('project graph transform:start');
let graph = (0, operators_1.pruneExternalNodes)(projectGraph);
// Apply focus and exclude filters
graph = filterGraph(graph, focus, exclude);
const fileMap = (0, nx_deps_cache_1.readFileMapCache)()?.fileMap.projectFileMap || {};
const layout = (0, configuration_1.workspaceLayout)();
const projects = Object.values(graph.nodes);
const dependencies = graph.dependencies;
const nxJson = (0, configuration_1.readNxJson)();
const connectedToCloud = (0, nx_cloud_utils_1.isNxCloudUsed)(nxJson);
const disabledTaskSyncGenerators = nxJson.sync?.disabledTaskSyncGenerators;
const hasher = (0, crypto_1.createHash)('sha256');
hasher.update(JSON.stringify({
layout,
projects,
dependencies,
sourceMaps,
connectedToCloud,
disabledTaskSyncGenerators,
}));
const hash = hasher.digest('hex');
node_perf_hooks_1.performance.mark('project graph transform:end');
node_perf_hooks_1.performance.measure('project graph transform', 'project graph transform:start', 'project graph transform:end');
return {
projectGraphClientResponse: {
...currentProjectGraphClientResponse,
hash,
layout,
projects,
dependencies,
affected,
fileMap,
isPartial: false,
errors,
connectedToCloud,
disabledTaskSyncGenerators,
},
sourceMapResponse: sourceMaps,
};
}
async function createProjectGraphAndSourceMapClientResponse(affected = [], focus = null, exclude = []) {
node_perf_hooks_1.performance.mark('project graph watch calculation:start');
let projectGraph;
let sourceMaps;
let errors;
try {
const projectGraphAndSourceMaps = await (0, project_graph_1.createProjectGraphAndSourceMapsAsync)({ exitOnError: false });
projectGraph = projectGraphAndSourceMaps.projectGraph;
sourceMaps = projectGraphAndSourceMaps.sourceMaps;
}
catch (e) {
if (e instanceof error_types_1.ProjectGraphError) {
projectGraph = e.getPartialProjectGraph();
sourceMaps = e.getPartialSourcemaps();
errors = e.getErrors().map((e) => ({
message: e.message,
stack: e.stack,
cause: e.cause,
name: e.name,
pluginName: e.pluginName,
fileName: e.file ?? e.cause?.errors?.[0]?.location?.file,
}));
}
if (!projectGraph) {
(0, project_graph_1.handleProjectGraphError)({ exitOnError: true }, e);
}
}
node_perf_hooks_1.performance.mark('project graph watch calculation:end');
node_perf_hooks_1.performance.mark('project graph response generation:start');
let { projectGraphClientResponse, sourceMapResponse } = transformProjectGraphToClientResponse(projectGraph, sourceMaps, errors, affected, focus, exclude);
node_perf_hooks_1.performance.mark('project graph response generation:end');
node_perf_hooks_1.performance.measure('project graph watch calculation', 'project graph watch calculation:start', 'project graph watch calculation:end');
node_perf_hooks_1.performance.measure('project graph response generation', 'project graph response generation:start', 'project graph response generation:end');
return {
projectGraphClientResponse,
sourceMapResponse,
};
}
async function createTaskGraphClientResponse(pruneExternal = false) {
let graph;
try {
graph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: false });
}
catch (e) {
if (e instanceof error_types_1.ProjectGraphError) {
graph = e.getPartialProjectGraph();
}
}
if (pruneExternal) {
graph = (0, operators_1.pruneExternalNodes)(graph);
}
const nxJson = (0, configuration_1.readNxJson)();
node_perf_hooks_1.performance.mark('task graph generation:start');
const projects = Object.keys(graph.nodes);
const allTargets = new Set();
for (const projectName in graph.nodes) {
const project = graph.nodes[projectName];
Object.keys(project.data.targets ?? {}).forEach((target) => {
allTargets.add(target);
});
}
const targets = Array.from(allTargets);
try {
const taskGraph = (0, create_task_graph_1.createTaskGraph)(graph, {}, projects, targets, undefined, {});
node_perf_hooks_1.performance.mark('task graph generation:end');
const planner = new native_1.HashPlanner(nxJson, (0, native_1.transferProjectGraph)((0, transform_objects_1.transformProjectGraphForRust)(graph)));
node_perf_hooks_1.performance.mark('task hash plan generation:start');
const taskIds = Object.keys(taskGraph.tasks);
const plans = taskIds.length > 0 ? planner.getPlans(taskIds, taskGraph) : {};
node_perf_hooks_1.performance.mark('task hash plan generation:end');
node_perf_hooks_1.performance.measure('task graph generation', 'task graph generation:start', 'task graph generation:end');
node_perf_hooks_1.performance.measure('task hash plan generation', 'task hash plan generation:start', 'task hash plan generation:end');
return { taskGraph, plans, error: null };
}
catch (err) {
node_perf_hooks_1.performance.mark('task graph generation:end');
node_perf_hooks_1.performance.measure('task graph generation (failed)', 'task graph generation:start', 'task graph generation:end');
return {
taskGraph: {
tasks: {},
dependencies: {},
continuousDependencies: {},
roots: [],
},
plans: {},
error: err.message,
};
}
}
async function createExpandedTaskInputResponse(taskGraphClientResponse, depGraphClientResponse) {
node_perf_hooks_1.performance.mark('task input static generation:start');
const allWorkspaceFiles = await (0, all_file_data_1.allFileData)();
const response = {};
Object.entries(taskGraphClientResponse.plans).forEach(([key, inputs]) => {
const [project] = key.split(':');
const expandedInputs = expandInputs(inputs, depGraphClientResponse.projects.find((p) => p.name === project), allWorkspaceFiles, depGraphClientResponse);
response[key] = expandedInputs;
});
node_perf_hooks_1.performance.mark('task input static generation:end');
node_perf_hooks_1.performance.measure('task input static generation', 'task input static generation:start', 'task input static generation:end');
return response;
}
// Performance optimized functions for lazy loading task graphs
// In-memory cache for task graphs to avoid regeneration
const taskGraphCache = new Map();
// In-memory cache for expanded task inputs to avoid regeneration
const expandedTaskInputsCache = new Map();
// Clear cache when project graph changes
function clearTaskGraphCache() {
taskGraphCache.clear();
expandedTaskInputsCache.clear();
}
/**
* Creates a single task graph for multiple projects with multiple targets
* If no projects specified, returns graph for all projects with the targets
*/
async function createTaskGraphForTargetsAndProjects(targetNames, projectNames, configuration) {
// Get project graph
let graph;
try {
graph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: false });
}
catch (e) {
if (e instanceof error_types_1.ProjectGraphError) {
graph = e.getPartialProjectGraph();
}
}
const nxJson = (0, configuration_1.readNxJson)();
node_perf_hooks_1.performance.mark(`task graph generation:start`);
let projectsToUse;
if (projectNames && projectNames.length > 0) {
projectsToUse = projectNames;
}
else {
// Get all projects that have at least one of the targets
projectsToUse = Object.entries(graph.nodes)
.filter(([_, project]) => targetNames.some((targetName) => project.data.targets?.[targetName]))
.map(([projectName]) => projectName);
}
try {
// Create single task graph
const taskGraph = (0, create_task_graph_1.createTaskGraph)(graph, {}, projectsToUse, targetNames, configuration, {});
node_perf_hooks_1.performance.mark(`task graph generation:end`);
const planner = new native_1.HashPlanner(nxJson, (0, native_1.transferProjectGraph)((0, transform_objects_1.transformProjectGraphForRust)(graph)));
node_perf_hooks_1.performance.mark('task hash plan generation:start');
const taskIds = Object.keys(taskGraph.tasks);
const plans = taskIds.length > 0 ? planner.getPlans(taskIds, taskGraph) : {};
node_perf_hooks_1.performance.mark('task hash plan generation:end');
node_perf_hooks_1.performance.measure(`task graph generation for ${targetNames.join(', ')}`, `task graph generation:start`, `task graph generation:end`);
node_perf_hooks_1.performance.measure('task hash plan generation', 'task hash plan generation:start', 'task hash plan generation:end');
return { taskGraph, plans, error: null };
}
catch (err) {
node_perf_hooks_1.performance.mark(`task graph generation:end`);
node_perf_hooks_1.performance.measure(`task graph generation for ${targetNames.join(', ')} (failed)`, `task graph generation:start`, `task graph generation:end`);
return {
taskGraph: {
tasks: {},
dependencies: {},
continuousDependencies: {},
roots: [],
},
plans: {},
error: err.message,
};
}
}
async function getExpandedTaskInputs(taskId) {
// Check cache first
if (expandedTaskInputsCache.has(taskId)) {
return expandedTaskInputsCache.get(taskId);
}
// Use the optimized version that only creates the specific task graph needed
const [projectName, targetName, configuration] = taskId.split(':');
const taskGraphResponse = await createTaskGraphForTargetsAndProjects([targetName], [projectName], configuration);
const allWorkspaceFiles = await (0, all_file_data_1.allFileData)();
const inputs = taskGraphResponse.plans?.[taskId];
let result = {};
if (inputs) {
result = expandInputs(inputs, currentProjectGraphClientResponse.projects.find((p) => p.name === projectName), allWorkspaceFiles, currentProjectGraphClientResponse);
}
// Cache the result
expandedTaskInputsCache.set(taskId, result);
return result;
}
function expandInputs(inputs, project, allWorkspaceFiles, depGraphClientResponse) {
const projectNames = depGraphClientResponse.projects.map((p) => p.name);
const workspaceRootInputs = [];
const projectRootInputs = [];
const externalInputs = [];
const otherInputs = [];
inputs.forEach((input) => {
// grouped workspace inputs look like workspace:[pattern,otherPattern]
if (input.startsWith('workspace:[')) {
const inputs = input.substring(11, input.length - 1).split(',');
workspaceRootInputs.push(...inputs);
return;
}
const maybeProjectName = input.split(':')[0];
if (projectNames.includes(maybeProjectName)) {
projectRootInputs.push(input);
return;
}
if (input === 'ProjectConfiguration' ||
input === 'TsConfig' ||
input === 'AllExternalDependencies') {
otherInputs.push(input);
return;
}
// there shouldn't be any other imports in here, but external ones are always going to have a modifier in front
if (input.includes(':')) {
externalInputs.push(input);
return;
}
});
const workspaceRootsExpanded = getExpandedWorkspaceRoots(workspaceRootInputs, allWorkspaceFiles);
const otherInputsExpanded = otherInputs.map((input) => {
if (input === 'TsConfig') {
return (0, node_path_1.relative)(workspace_root_1.workspaceRoot, (0, typescript_1.getRootTsConfigPath)());
}
if (input === 'ProjectConfiguration') {
return depGraphClientResponse.fileMap[project.name].find((file) => file.file === `${project.data.root}/project.json` ||
file.file === `${project.data.root}/package.json`).file;
}
return input;
});
const projectRootsExpanded = projectRootInputs
.map((input) => {
const fileSetProjectName = input.split(':')[0];
const fileSetProject = depGraphClientResponse.projects.find((p) => p.name === fileSetProjectName);
const fileSets = input.replace(`${fileSetProjectName}:`, '').split(',');
const projectInputExpanded = {
[fileSetProject.name]: (0, task_hasher_1.filterUsingGlobPatterns)(fileSetProject.data.root, depGraphClientResponse.fileMap[fileSetProject.name] || [], fileSets).map((f) => f.file),
};
return projectInputExpanded;
})
.reduce((curr, acc) => {
for (let key in curr) {
acc[key] = curr[key];
}
return acc;
}, {});
return {
general: [...workspaceRootsExpanded, ...otherInputsExpanded],
...projectRootsExpanded,
external: externalInputs,
};
}
function getExpandedWorkspaceRoots(workspaceRootInputs, allWorkspaceFiles) {
const workspaceRootsExpanded = [];
const negativeWRPatterns = [];
const positiveWRPatterns = [];
for (const fileset of workspaceRootInputs) {
if (fileset.startsWith('!')) {
negativeWRPatterns.push(fileset.substring(17));
}
else {
positiveWRPatterns.push(fileset.substring(16));
}
}
for (const pattern of positiveWRPatterns) {
const matchingFile = allWorkspaceFiles.find((t) => t.file === pattern);
if (matchingFile &&
!negativeWRPatterns.some((p) => (0, minimatch_1.minimatch)(matchingFile.file, p))) {
workspaceRootsExpanded.push(matchingFile.file);
}
else {
allWorkspaceFiles
.filter((f) => (0, minimatch_1.minimatch)(f.file, pattern) &&
!negativeWRPatterns.some((p) => (0, minimatch_1.minimatch)(f.file, p)))
.forEach((f) => {
workspaceRootsExpanded.push(f.file);
});
}
}
workspaceRootsExpanded.sort();
return workspaceRootsExpanded;
}
async function createJsonOutput(prunedGraph, rawGraph, projects, targets) {
const response = {
graph: prunedGraph,
};
if (targets?.length) {
const taskGraph = (0, create_task_graph_1.createTaskGraph)(rawGraph, {}, projects, targets, undefined, {});
const hasher = (0, create_task_hasher_1.createTaskHasher)(rawGraph, (0, configuration_1.readNxJson)());
let tasks = Object.values(taskGraph.tasks);
// Match the runtime path: each task is hashed against its own env so
// the graph-view hash matches the hash used when the task actually runs.
const perTaskEnvs = {};
for (const task of tasks) {
perTaskEnvs[task.id] = (0, task_env_1.getTaskSpecificEnv)(task, rawGraph);
}
const hashes = await hasher.hashTasks(tasks, taskGraph, perTaskEnvs);
response.tasks = taskGraph;
response.taskPlans = tasks.reduce((acc, task, index) => {
acc[task.id] = Object.keys(hashes[index].details.nodes).sort();
return acc;
}, {});
}
return response;
}
function getHelpTextFromTarget(projectName, targetName) {
if (!projectName)
throw new Error(`Missing project`);
if (!targetName)
throw new Error(`Missing target`);
const project = currentProjectGraphClientResponse.projects?.find((p) => p.name === projectName);
if (!project)
throw new Error(`Cannot find project ${projectName}`);
const target = project.data.targets[targetName];
if (!target)
throw new Error(`Cannot find target ${targetName}`);
const command = target.metadata?.help?.command;
if (!command)
throw new Error(`No help command found for ${projectName}:${targetName}`);
return (0, node_child_process_1.execSync)(command, {
cwd: target.options?.cwd ?? workspace_root_1.workspaceRoot,
windowsHide: true,
}).toString();
}

View File

@@ -0,0 +1,2 @@
import { CommandModule } from 'yargs';
export declare const yargsImportCommand: CommandModule;

View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.yargsImportCommand = void 0;
const native_1 = require("../../native");
const handle_errors_1 = require("../../utils/handle-errors");
const ai_output_1 = require("../ai/ai-output");
const ai_output_2 = require("./utils/ai-output");
const documentation_1 = require("../yargs-utils/documentation");
const shared_options_1 = require("../yargs-utils/shared-options");
const handle_import_1 = require("../../utils/handle-import");
exports.yargsImportCommand = {
command: 'import [sourceRepository] [destinationDirectory]',
describe: 'Import code and git history from another repository into this repository.',
builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)((0, shared_options_1.withVerbose)(yargs
.positional('sourceRepository', {
type: 'string',
description: 'The remote URL or local path of the source repository to import.',
})
.positional('destinationDirectory', {
type: 'string',
alias: 'destination',
description: 'The directory in the current workspace to import into.',
})
.option('sourceDirectory', {
type: 'string',
alias: 'source',
description: 'The directory in the source repository to import from.',
})
.option('ref', {
type: 'string',
description: 'The branch from the source repository to import.',
})
.option('depth', {
type: 'number',
description: 'The depth to clone the source repository (limit this for faster git clone).',
})
.option('interactive', {
type: 'boolean',
description: 'Interactive mode.',
default: true,
})
.option('plugins', {
type: 'string',
description: 'Plugins to install after import: "skip" for none, "all" for all detected, or comma-separated list (e.g., @nx/vite,@nx/jest).',
})), 'import'),
handler: async (args) => {
const exitCode = await (0, handle_errors_1.handleErrors)(args.verbose, async () => {
try {
return await (await (0, handle_import_1.handleImport)('./import.js', __dirname)).importHandler(args);
}
catch (error) {
if ((0, native_1.isAiAgent)()) {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorCode = (0, ai_output_2.determineImportErrorCode)(error);
const errorLogPath = (0, ai_output_2.writeErrorLog)(error, 'nx-import');
(0, ai_output_1.writeAiOutput)((0, ai_output_2.buildImportErrorResult)(errorMessage, errorCode, errorLogPath));
}
throw error;
}
});
process.exit(exitCode);
},
};

View File

@@ -0,0 +1,26 @@
export interface ImportOptions {
/**
* The remote URL of the repository to import
*/
sourceRepository: string;
/**
* The branch or reference to import
*/
ref: string;
/**
* The directory in the source repo to import
*/
source: string;
/**
* The directory in the destination repo to import into
*/
destination: string;
/**
* The depth to clone the source repository (limit this for faster clone times)
*/
depth: number;
verbose: boolean;
interactive: boolean;
plugins?: string;
}
export declare function importHandler(options: ImportOptions): Promise<void>;

600
node_modules/nx/dist/src/command-line/import/import.js generated vendored Normal file
View File

@@ -0,0 +1,600 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.importHandler = importHandler;
const tslib_1 = require("tslib");
const path_1 = require("path");
const node_fs_1 = require("node:fs");
const pc = tslib_1.__importStar(require("picocolors"));
const git_utils_1 = require("../../utils/git-utils");
const promises_1 = require("node:fs/promises");
const tmp_1 = require("tmp");
const enquirer_1 = require("enquirer");
const output_1 = require("../../utils/output");
const createSpinner = require('ora');
const init_v2_1 = require("../init/init-v2");
const nx_json_1 = require("../../config/nx-json");
const fileutils_1 = require("../../utils/fileutils");
const workspace_root_1 = require("../../utils/workspace-root");
const package_manager_1 = require("../../utils/package-manager");
const workspace_context_1 = require("../../utils/workspace-context");
const utils_1 = require("../init/implementation/utils");
const command_line_utils_1 = require("../../utils/command-line-utils");
const prepare_source_repo_1 = require("./utils/prepare-source-repo");
const merge_remote_source_1 = require("./utils/merge-remote-source");
const minimatch_1 = require("minimatch");
const configure_plugins_1 = require("../init/configure-plugins");
const check_compatible_with_plugins_1 = require("../init/implementation/check-compatible-with-plugins");
const native_1 = require("../../native");
const ai_output_1 = require("./utils/ai-output");
const init_v2_2 = require("../init/init-v2");
const importRemoteName = '__tmp_nx_import__';
async function importHandler(options) {
process.env.NX_RUNNING_NX_IMPORT = 'true';
let { sourceRepository, ref, source, destination, verbose } = options;
const aiMode = (0, native_1.isAiAgent)();
if (aiMode) {
options.interactive = false;
(0, ai_output_1.logProgress)('starting', 'Importing repository...');
// Check for missing required arguments — report all at once
const missingFields = [];
if (!sourceRepository)
missingFields.push('sourceRepository');
if (!ref)
missingFields.push('ref');
if (!destination)
missingFields.push('destination');
if (missingFields.length > 0) {
(0, ai_output_1.writeAiOutput)((0, ai_output_1.buildImportNeedsOptionsResult)(missingFields, sourceRepository));
process.exit(0);
}
// Check if this is a plugin-only call (second step of two-step flow)
if (destination && options.plugins) {
const absDestAi = (0, path_1.join)(process.cwd(), destination);
const destGitClient = new git_utils_1.GitRepository(process.cwd());
const destFiles = await destGitClient.getGitFiles(absDestAi);
if (destFiles.length > 0) {
// Destination not empty + --plugins provided + AI mode = plugin-only mode
return await handlePluginOnlyMode(options, destGitClient, verbose);
}
}
}
const destinationGitClient = new git_utils_1.GitRepository(process.cwd());
if (await destinationGitClient.hasUncommittedChanges()) {
throw new Error(`You have uncommitted changes in the destination repository. Commit or revert the changes and try again.`);
}
if (!aiMode) {
output_1.output.log({
title: 'Nx will walk you through the process of importing code from the source repository into this repository:',
bodyLines: [
`1. Nx will clone the source repository into a temporary directory`,
`2. The project code from the sourceDirectory will be moved to the destinationDirectory on a temporary branch in this repository`,
`3. The temporary branch will be merged into the current branch in this repository`,
`4. Nx will recommend plugins to integrate any new tools used in the imported code`,
'',
`Git history will be preserved during this process as long as you MERGE these changes. Do NOT squash and do NOT rebase the changes when merging branches. If you would like to UNDO these changes, run "git reset HEAD~1 --hard"`,
],
});
}
const tempImportDirectory = (0, path_1.join)(tmp_1.tmpdir, 'nx-import');
if (!sourceRepository) {
sourceRepository = (await (0, enquirer_1.prompt)([
{
type: 'input',
name: 'sourceRepository',
message: 'What is the URL of the repository you want to import? (This can be a local git repository or a git remote URL)',
required: true,
},
])).sourceRepository;
}
try {
const maybeLocalDirectory = await (0, promises_1.stat)(sourceRepository);
if (maybeLocalDirectory.isDirectory()) {
sourceRepository = (0, path_1.resolve)(sourceRepository);
}
}
catch (e) {
// It's a remote url
}
const sourceTempRepoPath = (0, path_1.join)(tempImportDirectory, 'repo');
let spinner;
if (aiMode) {
(0, ai_output_1.logProgress)('cloning', `Cloning ${sourceRepository} into ${sourceTempRepoPath}...`);
}
else {
spinner = createSpinner(`Cloning ${sourceRepository} into a temporary directory: ${sourceTempRepoPath} (Use --depth to limit commit history and speed up clone times)`).start();
}
try {
await (0, promises_1.rm)(tempImportDirectory, { recursive: true });
}
catch { }
await (0, promises_1.mkdir)(tempImportDirectory, { recursive: true });
let sourceGitClient;
try {
sourceGitClient = await (0, git_utils_1.cloneFromUpstream)(sourceRepository, sourceTempRepoPath, {
originName: importRemoteName,
depth: options.depth,
});
}
catch (e) {
if (!aiMode) {
spinner.fail(`Failed to clone ${sourceRepository} into ${sourceTempRepoPath}`);
}
let errorMessage = `Failed to clone ${sourceRepository} into ${sourceTempRepoPath}. Please double check the remote and try again.\n${e.message}`;
throw new Error(errorMessage);
}
if (!aiMode) {
spinner.succeed(`Cloned into ${sourceTempRepoPath}`);
}
// Detecting the package manager before preparing the source repo for import.
const sourcePackageManager = (0, package_manager_1.detectPackageManager)(sourceGitClient.root);
if (!ref) {
if (aiMode) {
throw new Error('The --ref option is required when running in agent mode.');
}
const branchChoices = await sourceGitClient.listBranches();
ref = (await (0, enquirer_1.prompt)([
{
type: 'autocomplete',
name: 'ref',
message: `Which branch do you want to import?`,
choices: branchChoices,
/**
* Limit the number of choices so that it fits on screen
*/
limit: process.stdout.rows - 3,
required: true,
},
])).ref;
}
if (!source) {
if (aiMode) {
// Default to importing the entire repository in agent mode
source = '.';
}
else {
source = (await (0, enquirer_1.prompt)([
{
type: 'input',
name: 'source',
message: `Which directory do you want to import into this workspace? (leave blank to import the entire repository)`,
},
])).source;
}
}
if (!destination) {
if (aiMode) {
throw new Error('The --destination option is required when running in agent mode.');
}
destination = (await (0, enquirer_1.prompt)([
{
type: 'input',
name: 'destination',
message: 'Where in this workspace should the code be imported into?',
required: true,
initial: source ? source : undefined,
},
])).destination;
}
const absSource = (0, path_1.join)(sourceTempRepoPath, source);
if ((0, path_1.isAbsolute)(destination)) {
throw new Error(`The destination directory must be a relative path in this repository.`);
}
const absDestination = (0, path_1.join)(process.cwd(), destination);
await assertDestinationEmpty(destinationGitClient, absDestination);
const tempImportBranch = getTempImportBranch(ref);
await sourceGitClient.addFetchRemote(importRemoteName, ref);
await sourceGitClient.fetch(importRemoteName, ref);
if (!aiMode) {
spinner.succeed(`Fetched ${ref} from ${sourceRepository}`);
spinner.start(`Checking out a temporary branch, ${tempImportBranch} based on ${ref}`);
}
await sourceGitClient.checkout(tempImportBranch, {
new: true,
base: `${importRemoteName}/${ref}`,
});
if (!aiMode) {
spinner.succeed(`Created a ${tempImportBranch} branch based on ${ref}`);
}
try {
await (0, promises_1.stat)(absSource);
}
catch (e) {
throw new Error(`The source directory ${source} does not exist in ${sourceRepository}. Please double check to make sure it exists.`);
}
const packageManager = (0, package_manager_1.detectPackageManager)(workspace_root_1.workspaceRoot);
const sourceIsNxWorkspace = (0, node_fs_1.existsSync)((0, path_1.join)(sourceGitClient.root, 'nx.json'));
const relativeDestination = (0, path_1.relative)(destinationGitClient.root, absDestination);
if (aiMode) {
(0, ai_output_1.logProgress)('filtering', 'Filtering git history...');
}
await (0, prepare_source_repo_1.prepareSourceRepo)(sourceGitClient, ref, source, relativeDestination, tempImportBranch, sourceRepository);
await createTemporaryRemote(destinationGitClient, (0, path_1.join)(sourceTempRepoPath, '.git'), importRemoteName);
if (aiMode) {
(0, ai_output_1.logProgress)('merging', 'Merging into workspace...');
}
await (0, merge_remote_source_1.mergeRemoteSource)(destinationGitClient, sourceRepository, tempImportBranch, destination, importRemoteName, ref);
if (!aiMode) {
spinner.start('Cleaning up temporary files and remotes');
}
await (0, promises_1.rm)(tempImportDirectory, { recursive: true });
await destinationGitClient.deleteGitRemote(importRemoteName);
if (!aiMode) {
spinner.succeed('Cleaned up temporary files and remotes');
}
const pmc = (0, package_manager_1.getPackageManagerCommand)();
const nxJson = (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot);
(0, workspace_context_1.resetWorkspaceContext)();
let packageJson;
try {
packageJson = (0, fileutils_1.readJsonFile)('package.json');
}
catch {
packageJson = null;
}
let plugins;
let updatePackageScripts;
let detectedButNotInstalled;
if (aiMode) {
(0, ai_output_1.logProgress)('detecting-plugins', 'Checking for recommended plugins...');
const parsedPlugins = parsePluginsFlag(options.plugins);
if (parsedPlugins === 'skip') {
plugins = [];
updatePackageScripts = false;
}
else if (parsedPlugins === 'all') {
const detected = await (0, init_v2_1.detectPlugins)(nxJson, packageJson, false, true);
plugins = detected.plugins;
updatePackageScripts = detected.updatePackageScripts;
}
else if (Array.isArray(parsedPlugins)) {
plugins = parsedPlugins;
updatePackageScripts = true;
}
else {
// No --plugins flag: detect and report, let agent decide
const detected = await (0, init_v2_1.detectPlugins)(nxJson, packageJson, false, true);
if (detected.plugins.length > 0) {
detectedButNotInstalled = detected.plugins;
}
plugins = [];
updatePackageScripts = false;
}
}
else {
const detected = await (0, init_v2_1.detectPlugins)(nxJson, packageJson, options.interactive, true);
plugins = detected.plugins;
updatePackageScripts = detected.updatePackageScripts;
}
if (!aiMode && packageManager !== sourcePackageManager) {
output_1.output.warn({
title: `Mismatched package managers`,
bodyLines: [
`The source repository is using a different package manager (${sourcePackageManager}) than this workspace (${packageManager}).`,
`This could lead to install issues due to discrepancies in "package.json" features.`,
],
});
}
await handleMissingWorkspacesEntry(packageManager, pmc, relativeDestination, destinationGitClient);
let installed = await runInstallDestinationRepo(packageManager, destinationGitClient);
if (installed) {
// Check compatibility with existing plugins for the workspace included new imported projects
if (nxJson.plugins?.length > 0) {
const incompatiblePlugins = await (0, check_compatible_with_plugins_1.checkCompatibleWithPlugins)();
if (Object.keys(incompatiblePlugins).length > 0) {
(0, check_compatible_with_plugins_1.updatePluginsInNxJson)(workspace_root_1.workspaceRoot, incompatiblePlugins);
await destinationGitClient.amendCommit();
}
}
if (plugins.length > 0) {
installed = await runPluginsInstall(plugins, pmc, destinationGitClient);
if (installed) {
const { succeededPlugins } = await (0, configure_plugins_1.configurePlugins)(plugins, updatePackageScripts, pmc, workspace_root_1.workspaceRoot, verbose);
if (succeededPlugins.length > 0) {
await destinationGitClient.amendCommit();
}
}
}
}
console.log(await destinationGitClient.showStat());
if (!aiMode && installed === false) {
const pmc = (0, package_manager_1.getPackageManagerCommand)(packageManager);
output_1.output.warn({
title: `The import was successful, but the install failed`,
bodyLines: [
`You may need to run "${pmc.install}" manually to resolve the issue. The error is logged above.`,
],
});
if (plugins.length > 0) {
output_1.output.error({
title: `Failed to install plugins`,
bodyLines: [
'The following plugins were not installed:',
...plugins.map((p) => `- ${pc.bold(p)}`),
],
});
output_1.output.error({
title: `To install the plugins manually`,
bodyLines: [
'You may need to run commands to install the plugins:',
...plugins.map((p) => `- ${pc.bold(pmc.exec + ' nx add ' + p)}`),
],
});
}
}
if (!aiMode && source != destination) {
output_1.output.warn({
title: `Check configuration files`,
bodyLines: [
`The source directory (${source}) and destination directory (${destination}) are different.`,
`You may need to update configuration files to match the directory in this repository.`,
sourceIsNxWorkspace
? `For example, path options in project.json such as "main", "tsConfig", and "outputPath" need to be updated.`
: `For example, relative paths in tsconfig.json and other tooling configuration files may need to be updated.`,
],
});
}
// When only a subdirectory is imported, there might be devDependencies in the root package.json file
// that needs to be ported over as well.
if (!aiMode && ref) {
output_1.output.log({
title: `Check root dependencies`,
bodyLines: [
`"dependencies" and "devDependencies" are not imported from the source repository (${sourceRepository}).`,
`You may need to add some of those dependencies to this workspace in order to run tasks successfully.`,
],
});
}
if (!aiMode) {
output_1.output.log({
title: `Merging these changes into ${(0, command_line_utils_1.getBaseRef)(nxJson)}`,
bodyLines: [
`MERGE these changes when merging these changes.`,
`Do NOT squash these commits when merging these changes.`,
`If you rebase, make sure to use "--rebase-merges" to preserve merge commits.`,
`To UNDO these changes, run "git reset HEAD~1 --hard"`,
],
});
}
if (aiMode) {
if (detectedButNotInstalled && detectedButNotInstalled.length > 0) {
// Import is done but plugins need selection — return needs_input
(0, ai_output_1.writeAiOutput)((0, ai_output_1.buildImportNeedsPluginSelectionResult)({
detectedPlugins: detectedButNotInstalled.map((name) => ({
name,
reason: (0, init_v2_2.getPluginReason)(name),
})),
sourceRepository,
ref,
source: source || '.',
destination,
}));
}
else {
const warnings = [];
if (packageManager !== sourcePackageManager) {
warnings.push({
type: 'package_manager_mismatch',
message: `Source uses ${sourcePackageManager}, workspace uses ${packageManager}`,
hint: 'Check for package.json feature discrepancies',
});
}
if (source !== destination) {
warnings.push({
type: 'config_path_mismatch',
message: `Source directory (${source}) differs from destination (${destination})`,
hint: 'Update relative paths in configuration files (tsconfig.json, project.json, etc.)',
});
}
if (ref) {
warnings.push({
type: 'missing_root_deps',
message: 'Root dependencies and devDependencies are not imported',
hint: 'Manually add required dependencies from the source repository',
});
}
if (!installed) {
warnings.push({
type: 'install_failed',
message: 'Package installation failed after import',
hint: `Run "${pmc.install}" manually to resolve`,
});
}
(0, ai_output_1.writeAiOutput)((0, ai_output_1.buildImportSuccessResult)({
sourceRepository,
ref,
source: source || '.',
destination,
pluginsInstalled: plugins.filter(() => installed),
warnings: warnings.length > 0 ? warnings : undefined,
}));
}
}
}
async function assertDestinationEmpty(gitClient, absDestination) {
const files = await gitClient.getGitFiles(absDestination);
if (files.length > 0) {
throw new Error(`Destination directory ${absDestination} is not empty. Please make sure it is empty before importing into it.`);
}
}
/**
* Handle the plugin-only mode (second call in two-step AI flow).
* Destination already has imported code, just install plugins.
*/
async function handlePluginOnlyMode(options, destinationGitClient, verbose) {
(0, ai_output_1.logProgress)('installing-plugins', 'Installing plugins for imported project...');
const pmc = (0, package_manager_1.getPackageManagerCommand)();
const nxJson = (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot);
let packageJson;
try {
packageJson = (0, fileutils_1.readJsonFile)('package.json');
}
catch {
packageJson = null;
}
const parsedPlugins = parsePluginsFlag(options.plugins);
let plugins;
let updatePackageScripts;
if (parsedPlugins === 'skip') {
plugins = [];
updatePackageScripts = false;
}
else if (parsedPlugins === 'all') {
const detected = await (0, init_v2_1.detectPlugins)(nxJson, packageJson, false, true);
plugins = detected.plugins;
updatePackageScripts = detected.updatePackageScripts;
}
else if (Array.isArray(parsedPlugins)) {
plugins = parsedPlugins;
updatePackageScripts = true;
}
else {
plugins = [];
updatePackageScripts = false;
}
if (plugins.length > 0) {
const installed = await runPluginsInstall(plugins, pmc, destinationGitClient);
if (installed) {
const { succeededPlugins } = await (0, configure_plugins_1.configurePlugins)(plugins, updatePackageScripts, pmc, workspace_root_1.workspaceRoot, verbose);
if (succeededPlugins.length > 0) {
await destinationGitClient.amendCommit();
}
}
}
(0, ai_output_1.writeAiOutput)((0, ai_output_1.buildImportSuccessResult)({
sourceRepository: options.sourceRepository,
ref: options.ref,
source: options.source || '.',
destination: options.destination,
pluginsInstalled: plugins,
}));
}
function getTempImportBranch(sourceBranch) {
return `__nx_tmp_import__/${sourceBranch}`;
}
async function createTemporaryRemote(destinationGitClient, sourceRemoteUrl, remoteName) {
try {
await destinationGitClient.deleteGitRemote(remoteName);
}
catch { }
await destinationGitClient.addGitRemote(remoteName, sourceRemoteUrl);
await destinationGitClient.fetch(remoteName);
}
/**
* Run install for the imported code and plugins
* @returns true if the install failed
*/
async function runInstallDestinationRepo(packageManager, destinationGitClient) {
let installed = true;
try {
output_1.output.log({
title: 'Installing dependencies for imported code',
});
(0, utils_1.runInstall)(workspace_root_1.workspaceRoot, (0, package_manager_1.getPackageManagerCommand)(packageManager));
await destinationGitClient.amendCommit();
}
catch (e) {
installed = false;
output_1.output.error({
title: `Install failed: ${e.message || 'Unknown error'}`,
bodyLines: [e.stack],
});
}
return installed;
}
async function runPluginsInstall(plugins, pmc, destinationGitClient) {
let installed = true;
output_1.output.log({ title: 'Installing Plugins' });
try {
(0, configure_plugins_1.installPluginPackages)(workspace_root_1.workspaceRoot, pmc, plugins);
await destinationGitClient.amendCommit();
}
catch (e) {
installed = false;
output_1.output.error({
title: `Install failed: ${e.message || 'Unknown error'}`,
bodyLines: [
'The following plugins were not installed:',
...plugins.map((p) => `- ${pc.bold(p)}`),
e.stack,
],
});
output_1.output.error({
title: `To install the plugins manually`,
bodyLines: [
'You may need to run commands to install the plugins:',
...plugins.map((p) => `- ${pc.bold(pmc.exec + ' nx add ' + p)}`),
],
});
}
return installed;
}
function parsePluginsFlag(value) {
if (value === undefined) {
return undefined;
}
if (value === 'skip') {
return 'skip';
}
if (value === 'all') {
return 'all';
}
return value
.split(',')
.map((p) => p.trim())
.filter(Boolean);
}
/*
* 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(pm, pmc, pkgPath, destinationGitClient) {
if (!(0, package_manager_1.isWorkspacesEnabled)(pm, workspace_root_1.workspaceRoot)) {
output_1.output.warn({
title: `Missing workspaces in package.json`,
bodyLines: pm === 'npm'
? [
`We recommend enabling NPM workspaces to install dependencies for the imported project.`,
`Add \`"workspaces": ["${pkgPath}"]\` to package.json and run "${pmc.install}".`,
`See: https://docs.npmjs.com/cli/using-npm/workspaces`,
]
: pm === 'yarn'
? [
`We recommend enabling Yarn workspaces to install dependencies for the imported project.`,
`Add \`"workspaces": ["${pkgPath}"]\` to package.json and run "${pmc.install}".`,
`See: https://yarnpkg.com/features/workspaces`,
]
: pm === 'bun'
? [
`We recommend enabling Bun workspaces to install dependencies for the imported project.`,
`Add \`"workspaces": ["${pkgPath}"]\` to package.json and run "${pmc.install}".`,
`See: https://bun.sh/docs/install/workspaces`,
]
: [
`We recommend enabling PNPM workspaces to install dependencies for the imported project.`,
`Add the following entry to to pnpm-workspace.yaml and run "${pmc.install}":`,
pc.bold(`packages:\n - '${pkgPath}'`),
`See: https://pnpm.io/workspaces`,
],
});
}
else {
let workspaces = (0, package_manager_1.getPackageWorkspaces)(pm, workspace_root_1.workspaceRoot);
const isPkgIncluded = workspaces.some((w) => (0, minimatch_1.minimatch)(pkgPath, w));
if (isPkgIncluded) {
return;
}
(0, package_manager_1.addPackagePathToWorkspaces)(pkgPath, pm, workspaces, workspace_root_1.workspaceRoot);
await destinationGitClient.amendCommit();
output_1.output.success({
title: `Project added in workspaces`,
bodyLines: pm === 'npm' || pm === 'yarn' || pm === 'bun'
? [
`The imported project (${pc.bold(pkgPath)}) is missing the "workspaces" field in package.json.`,
`Added "${pc.bold(pkgPath)}" to workspaces.`,
]
: [
`The imported project (${pc.bold(pkgPath)}) is missing the "packages" field in pnpm-workspaces.yaml.`,
`Added "${pc.bold(pkgPath)}" to packages.`,
],
});
}
}

View File

@@ -0,0 +1,93 @@
/**
* AI Agent NDJSON Output Utilities for nx import
*
* Extends the shared base with import-specific types and builders.
*/
import { writeAiOutput, logProgress, writeErrorLog, type DetectedPlugin, type UserNextSteps } from '../../ai/ai-output';
export { writeAiOutput, logProgress, writeErrorLog };
export type ImportProgressStage = 'starting' | 'cloning' | 'filtering' | 'merging' | 'detecting-plugins' | 'installing' | 'installing-plugins' | 'complete' | 'error' | 'needs_input';
export type NxImportErrorCode = 'UNCOMMITTED_CHANGES' | 'CLONE_FAILED' | 'SOURCE_NOT_FOUND' | 'DESTINATION_NOT_EMPTY' | 'INVALID_DESTINATION' | 'FILTER_FAILED' | 'MERGE_FAILED' | 'PACKAGE_INSTALL_ERROR' | 'PLUGIN_INIT_ERROR' | 'UNKNOWN';
interface ImportOptionInfo {
description: string;
flag: string;
required: boolean;
}
export interface ImportNeedsOptionsResult {
stage: 'needs_input';
success: false;
inputType: 'import_options';
message: string;
missingFields: string[];
availableOptions: Record<string, ImportOptionInfo>;
exampleCommand: string;
}
export interface ImportNeedsPluginSelectionResult {
stage: 'needs_input';
success: false;
inputType: 'plugins';
message: string;
detectedPlugins: DetectedPlugin[];
options: string[];
recommendedOption: string;
recommendedReason: string;
exampleCommand: string;
result: {
sourceRepository: string;
ref: string;
source: string;
destination: string;
};
}
export interface ImportSuccessResult {
stage: 'complete';
success: true;
result: {
sourceRepository: string;
ref: string;
source: string;
destination: string;
pluginsInstalled: string[];
};
warnings?: ImportWarning[];
userNextSteps: UserNextSteps;
docs: {
gettingStarted: string;
nxImport: string;
};
}
export interface ImportWarning {
type: 'package_manager_mismatch' | 'config_path_mismatch' | 'missing_root_deps' | 'install_failed' | 'plugin_install_failed';
message: string;
hint: string;
}
export interface ImportErrorResult {
stage: 'error';
success: false;
errorCode: NxImportErrorCode;
error: string;
hints: string[];
errorLogPath?: string;
}
export type ImportAiOutputMessage = {
stage: ImportProgressStage;
message: string;
} | ImportNeedsOptionsResult | ImportNeedsPluginSelectionResult | ImportSuccessResult | ImportErrorResult;
export declare function buildImportNeedsOptionsResult(missingFields: string[], sourceRepository?: string): ImportNeedsOptionsResult;
export declare function buildImportNeedsPluginSelectionResult(options: {
detectedPlugins: DetectedPlugin[];
sourceRepository: string;
ref: string;
source: string;
destination: string;
}): ImportNeedsPluginSelectionResult;
export declare function buildImportSuccessResult(options: {
sourceRepository: string;
ref: string;
source: string;
destination: string;
pluginsInstalled: string[];
warnings?: ImportWarning[];
}): ImportSuccessResult;
export declare function buildImportErrorResult(error: string, errorCode: NxImportErrorCode, errorLogPath?: string): ImportErrorResult;
export declare function getImportErrorHints(errorCode: NxImportErrorCode): string[];
export declare function determineImportErrorCode(error: Error | unknown): NxImportErrorCode;

View File

@@ -0,0 +1,209 @@
"use strict";
/**
* AI Agent NDJSON Output Utilities for nx import
*
* Extends the shared base with import-specific types and builders.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeErrorLog = exports.logProgress = exports.writeAiOutput = void 0;
exports.buildImportNeedsOptionsResult = buildImportNeedsOptionsResult;
exports.buildImportNeedsPluginSelectionResult = buildImportNeedsPluginSelectionResult;
exports.buildImportSuccessResult = buildImportSuccessResult;
exports.buildImportErrorResult = buildImportErrorResult;
exports.getImportErrorHints = getImportErrorHints;
exports.determineImportErrorCode = determineImportErrorCode;
const ai_output_1 = require("../../ai/ai-output");
Object.defineProperty(exports, "writeAiOutput", { enumerable: true, get: function () { return ai_output_1.writeAiOutput; } });
Object.defineProperty(exports, "logProgress", { enumerable: true, get: function () { return ai_output_1.logProgress; } });
Object.defineProperty(exports, "writeErrorLog", { enumerable: true, get: function () { return ai_output_1.writeErrorLog; } });
const AVAILABLE_OPTIONS = {
sourceRepository: {
description: 'URL or path of the repository to import.',
flag: '--sourceRepository',
required: true,
},
ref: {
description: 'Branch to import from the source repository.',
flag: '--ref',
required: true,
},
source: {
description: 'Directory within the source repo to import (blank = entire repo).',
flag: '--source',
required: false,
},
destination: {
description: 'Target directory in this workspace to import into.',
flag: '--destination',
required: true,
},
};
function buildImportNeedsOptionsResult(missingFields, sourceRepository) {
const exampleRepo = sourceRepository || 'https://github.com/org/repo';
return {
stage: 'needs_input',
success: false,
inputType: 'import_options',
message: 'Required options missing. Re-invoke with the listed flags.',
missingFields,
availableOptions: AVAILABLE_OPTIONS,
exampleCommand: `nx import ${exampleRepo} --ref=main --source=apps/my-app --destination=apps/my-app`,
};
}
function buildImportNeedsPluginSelectionResult(options) {
const pluginList = options.detectedPlugins.map((p) => p.name).join(',');
return {
stage: 'needs_input',
success: false,
inputType: 'plugins',
message: 'Import complete. Plugin selection required. Ask the user which plugins to install, then run again with --plugins flag.',
detectedPlugins: options.detectedPlugins,
options: ['--plugins=skip', '--plugins=all', `--plugins=${pluginList}`],
recommendedOption: '--plugins=all',
recommendedReason: 'Installing all detected plugins ensures the imported project works correctly with Nx.',
exampleCommand: `nx import ${options.sourceRepository} ${options.destination} --ref=${options.ref} --source=${options.source} --plugins=${options.detectedPlugins[0]?.name || '@nx/vite'}`,
result: {
sourceRepository: options.sourceRepository,
ref: options.ref,
source: options.source,
destination: options.destination,
},
};
}
function buildImportSuccessResult(options) {
const steps = [
{
title: 'Explore your workspace',
command: 'nx graph',
note: 'Visualize project dependencies including imported projects',
},
{
title: 'List imported projects',
command: 'nx show projects',
note: 'Verify the imported projects appear in the workspace',
},
{
title: 'Run a task on imported code',
command: `nx run <project>:<target>`,
note: 'Test that imported projects build and run correctly',
},
];
const result = {
stage: 'complete',
success: true,
result: {
sourceRepository: options.sourceRepository,
ref: options.ref,
source: options.source,
destination: options.destination,
pluginsInstalled: options.pluginsInstalled,
},
userNextSteps: {
description: 'Show user these steps to verify the import.',
steps,
},
docs: {
gettingStarted: 'https://nx.dev/getting-started/intro',
nxImport: 'https://nx.dev/nx-api/nx/documents/import',
},
};
if (options.warnings && options.warnings.length > 0) {
result.warnings = options.warnings;
}
return result;
}
function buildImportErrorResult(error, errorCode, errorLogPath) {
return {
stage: 'error',
success: false,
errorCode,
error,
hints: getImportErrorHints(errorCode),
errorLogPath,
};
}
function getImportErrorHints(errorCode) {
switch (errorCode) {
case 'UNCOMMITTED_CHANGES':
return [
'Commit or stash your changes before running nx import',
'Run "git status" to see uncommitted changes',
];
case 'CLONE_FAILED':
return [
'Check the repository URL is correct and accessible',
'Ensure you have the necessary permissions to clone',
'For local paths, verify the directory exists',
];
case 'SOURCE_NOT_FOUND':
return [
'The specified source directory does not exist in the source repository',
'Check the directory path and branch name',
'Omit --source to import the entire repository',
];
case 'DESTINATION_NOT_EMPTY':
return [
'The destination directory already contains files',
'Choose a different destination or remove existing files',
];
case 'INVALID_DESTINATION':
return [
'The destination must be a relative path within the workspace',
'Do not use absolute paths',
];
case 'FILTER_FAILED':
return [
'Git history filtering failed',
'Install git-filter-repo for faster and more reliable filtering: pip install git-filter-repo',
'Check that the source repository has valid git history',
];
case 'MERGE_FAILED':
return [
'Merging the imported code failed',
'Check for conflicts between source and destination',
'Run "git status" to see the current state',
];
case 'PACKAGE_INSTALL_ERROR':
return [
'Package installation failed after import',
'Run your package manager install manually',
'Check for dependency conflicts between imported and existing packages',
];
case 'PLUGIN_INIT_ERROR':
return [
'One or more plugin initializations failed',
'Try running "nx add <plugin>" manually',
];
default:
return [
'An unexpected error occurred during import',
'Check the error log for details',
'Report issues at https://github.com/nrwl/nx/issues',
];
}
}
function determineImportErrorCode(error) {
const message = error instanceof Error ? error.message : String(error);
const lower = message.toLowerCase();
if (lower.includes('uncommitted'))
return 'UNCOMMITTED_CHANGES';
if (lower.includes('failed to clone'))
return 'CLONE_FAILED';
if (lower.includes('does not exist in'))
return 'SOURCE_NOT_FOUND';
if (lower.includes('is not empty') || lower.includes('destination directory'))
return 'DESTINATION_NOT_EMPTY';
if (lower.includes('must be a relative path'))
return 'INVALID_DESTINATION';
if (lower.includes('filter-repo') ||
lower.includes('filter-branch') ||
lower.includes('filter'))
return 'FILTER_FAILED';
if (lower.includes('merge'))
return 'MERGE_FAILED';
if (lower.includes('install'))
return 'PACKAGE_INSTALL_ERROR';
if (lower.includes('plugin') || lower.includes('generator'))
return 'PLUGIN_INIT_ERROR';
return 'UNKNOWN';
}

View File

@@ -0,0 +1,2 @@
import { GitRepository } from '../../../utils/git-utils';
export declare function mergeRemoteSource(destinationGitClient: GitRepository, sourceRemoteUrl: string, tempBranch: string, destination: string, remoteName: string, branchName: string): Promise<void>;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeRemoteSource = mergeRemoteSource;
const createSpinner = require('ora');
async function mergeRemoteSource(destinationGitClient, sourceRemoteUrl, tempBranch, destination, remoteName, branchName) {
const spinner = createSpinner();
spinner.start(`Merging ${branchName} from ${sourceRemoteUrl} into ${destination}`);
spinner.start(`Fetching ${tempBranch} from ${remoteName}`);
await destinationGitClient.fetch(remoteName, tempBranch);
spinner.succeed(`Fetched ${tempBranch} from ${remoteName}`);
spinner.start(`Merging files and git history from ${branchName} from ${sourceRemoteUrl} into ${destination}`);
await destinationGitClient.mergeUnrelatedHistories(`${remoteName}/${tempBranch}`, `feat(repo): merge ${branchName} from ${sourceRemoteUrl}`);
spinner.succeed(`Merged files and git history from ${branchName} from ${sourceRemoteUrl} into ${destination}`);
}

View File

@@ -0,0 +1,2 @@
import { GitRepository } from '../../../utils/git-utils';
export declare function prepareSourceRepo(gitClient: GitRepository, ref: string, source: string, relativeDestination: string, tempImportBranch: string, sourceRemoteUrl: string): Promise<void>;

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.prepareSourceRepo = prepareSourceRepo;
const createSpinner = require('ora');
const path_1 = require("path");
async function prepareSourceRepo(gitClient, ref, source, relativeDestination, tempImportBranch, sourceRemoteUrl) {
const spinner = createSpinner().start(`Fetching ${ref} from ${sourceRemoteUrl}`);
const relativeSourceDir = (0, path_1.relative)(gitClient.root, (0, path_1.join)(gitClient.root, source));
const message = relativeSourceDir.trim()
? `Filtering git history to only include files in ${relativeSourceDir}`
: `Filtering git history`;
if (await gitClient.hasFilterRepoInstalled()) {
spinner.start(message);
await gitClient.filterRepo(relativeSourceDir, relativeDestination);
}
else {
spinner.start(`${message} (this might take a few minutes -- install git-filter-repo for faster performance)`);
await gitClient.filterBranch(relativeSourceDir, relativeDestination, tempImportBranch);
}
spinner.succeed(relativeSourceDir.trim()
? `Filtered git history to only include files in ${relativeSourceDir}`
: `Filtered git history`);
spinner.succeed(`${sourceRemoteUrl} has been prepared to be imported into this workspace on a temporary branch: ${tempImportBranch} in ${gitClient.root}`);
}
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

22
node_modules/nx/dist/src/command-line/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,22 @@
export * from './add/command-object';
export * from './affected/command-object';
export * from './daemon/command-object';
export * from './exec/command-object';
export * from './format/command-object';
export * from './generate/command-object';
export * from './graph/command-object';
export * from './import/command-object';
export * from './init/command-object';
export * from './list/command-object';
export * from './migrate/command-object';
export * from './new/command-object';
export * from './register/command-object';
export * from './release/command-object';
export * from './repair/command-object';
export * from './report/command-object';
export * from './reset/command-object';
export * from './run-many/command-object';
export * from './run/command-object';
export * from './show/command-object';
export * from './sync/command-object';
export * from './watch/command-object';

26
node_modules/nx/dist/src/command-line/index.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
// CLI Commands Export File for Documentation
tslib_1.__exportStar(require("./add/command-object"), exports);
tslib_1.__exportStar(require("./affected/command-object"), exports);
tslib_1.__exportStar(require("./daemon/command-object"), exports);
tslib_1.__exportStar(require("./exec/command-object"), exports);
tslib_1.__exportStar(require("./format/command-object"), exports);
tslib_1.__exportStar(require("./generate/command-object"), exports);
tslib_1.__exportStar(require("./graph/command-object"), exports);
tslib_1.__exportStar(require("./import/command-object"), exports);
tslib_1.__exportStar(require("./init/command-object"), exports);
tslib_1.__exportStar(require("./list/command-object"), exports);
tslib_1.__exportStar(require("./migrate/command-object"), exports);
tslib_1.__exportStar(require("./new/command-object"), exports);
tslib_1.__exportStar(require("./register/command-object"), exports);
tslib_1.__exportStar(require("./release/command-object"), exports);
tslib_1.__exportStar(require("./repair/command-object"), exports);
tslib_1.__exportStar(require("./report/command-object"), exports);
tslib_1.__exportStar(require("./reset/command-object"), exports);
tslib_1.__exportStar(require("./run-many/command-object"), exports);
tslib_1.__exportStar(require("./run/command-object"), exports);
tslib_1.__exportStar(require("./show/command-object"), exports);
tslib_1.__exportStar(require("./sync/command-object"), exports);
tslib_1.__exportStar(require("./watch/command-object"), exports);

View File

@@ -0,0 +1,2 @@
import { Agent } from '../../ai/utils';
export declare function determineAiAgents(aiAgents?: Agent[], interactive?: boolean): Promise<Agent[]>;

Some files were not shown because too many files have changed in this diff Show More