Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions packages/devkit/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# @nx/devkit

## Architecture

`@nx/devkit` serves two purposes: it **re-exports** core types and utilities from the `nx` package, and it **defines its own utilities** that are useful for plugin authors but aren't needed by `nx` core itself.

### Entry Point Structure

```
@nx/devkit (index.ts)
├── re-exports: nx/src/devkit-exports (stable public API)
├── exports: ./public-api (plugin-author utilities defined in devkit)
│ └── may import from: nx/src/devkit-internals (NOT re-exported to consumers)
├── @nx/devkit/testing → nx/src/devkit-testing-exports
├── @nx/devkit/ngcli-adapter → nx/src/adapter/ngcli-adapter
└── @nx/devkit/internal → nx/src/devkit-internals (subset)
```

## Version Compatibility Contract

**This is the most important thing to understand when modifying devkit or its nx entry points.**

`@nx/devkit` supports `nx` at the current major version **+/- 1 major version**. The `peerDependencies` in `package.json` encode this — e.g. `"nx": ">= 21 <= 23"` means `@nx/devkit@22` works with `nx@21`, `nx@22`, and `nx@23`.

### What This Means for Changes

#### `nx/src/devkit-exports.ts` (the public API surface)

- Everything exported here becomes the public API of `@nx/devkit`.
- **Minimize additions** — the file has a warning: "STOP! Try hard to not add to this API."
- New exports are safe for current consumers but adding then removing them creates breaking changes.

#### `nx/src/devkit-internals.ts` (semi-private bridge)

- These are used **internally** by `@nx/devkit`'s own implementation code (e.g. `packages/devkit/src/`).
- They are **NOT** part of `@nx/devkit`'s public API.
- The file warns: "These may not be available in certain versions of Nx, so be sure to check them first."
- **When `@nx/devkit` code imports from `devkit-internals`, it must handle the case where that export doesn't exist** in an older supported `nx` version. Guard with runtime checks or ensure the export has existed since the oldest supported major.

#### `packages/devkit/public-api.ts` (plugin-author utilities owned by devkit)

- Utilities implemented in `packages/devkit/src/` that are useful for plugin authors but not needed by `nx` core (e.g. `formatFiles`, `generateFiles`, `parseTargetString`).
- These are **defined here, not re-exported from `nx`** — devkit is the source of truth for this code.
- Code here may import from `nx/src/devkit-internals` — same version-guarding rules apply.

### Practical Guidelines

1. **Adding a new export to `devkit-exports.ts`**: This is a public API addition. Keep the surface area small. Once published, removing it is a breaking change.
2. **Adding a new export to `devkit-internals.ts`**: Safe to add, but any `@nx/devkit` code consuming it must account for older `nx` versions where it won't exist.
3. **Removing an export from either file**: Only safe if no published `@nx/devkit` version within the supported range depends on it.
4. **Changing the signature of an existing export**: Must remain compatible across all supported `nx` major versions.

## Key Files

| File | Purpose |
| ------------------------------------------- | -------------------------------------------------------------------------- |
| `packages/devkit/index.ts` | Main entry point — re-exports from `nx` + `public-api` |
| `packages/devkit/public-api.ts` | Plugin-author utilities owned by devkit (formatFiles, generateFiles, etc.) |
| `packages/nx/src/devkit-exports.ts` | Stable public API surface exposed through `@nx/devkit` |
| `packages/nx/src/devkit-internals.ts` | Semi-private internals used by devkit's implementation |
| `packages/nx/src/devkit-testing-exports.ts` | Testing utilities for `@nx/devkit/testing` |
8 changes: 7 additions & 1 deletion packages/devkit/src/executors/parse-target-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,15 @@ export function parseTargetString(
targetString = `${projectGraphOrCtx.projectName}:${targetString}`;
}

const currentProject =
projectGraphOrCtx && 'projectName' in projectGraphOrCtx
? projectGraphOrCtx.projectName
: undefined;

const [project, target, configuration] = splitTarget(
targetString,
projectGraph
projectGraph,
{ currentProject }
);

if (!project || !target) {
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/adapter/ngcli-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ import {
resolveImplementation,
resolveSchema,
} from '../config/schema-utils';
import { resolveNxTokensInOptions } from '../project-graph/utils/project-configuration-utils';
import { handleImport } from '../utils/handle-import';
import { resolveNxTokensInOptions } from '../project-graph/utils/project-configuration/target-merging';

function getProjectGraph(): Promise<ProjectGraph> {
try {
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { transformProjectGraphForRust } from '../../native/transform-objects';
import { getAffectedGraphNodes } from '../affected/affected';
import { readFileMapCache } from '../../project-graph/nx-deps-cache';
import { filterUsingGlobPatterns } from '../../hasher/task-hasher';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration/source-maps';
import { findMatchingProjects } from '../../utils/find-matching-projects';

import { createTaskHasher } from '../../hasher/create-task-hasher';
Expand Down
3 changes: 2 additions & 1 deletion packages/nx/src/command-line/run/run-one.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ export function parseRunOneOptions(
// run case
[project, target, configuration] = splitTarget(
parsedArgs[PROJECT_TARGET_CONFIG],
projectGraph
projectGraph,
{ currentProject: defaultProjectName }
);
// this is to account for "nx npmscript:dev"
if (project && !target && defaultProjectName) {
Expand Down
18 changes: 11 additions & 7 deletions packages/nx/src/command-line/show/target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,16 @@ function resolveTargetIdentifier(
process.exit(1);
}

const [project, target, config] = splitTarget(args.target, graph);
const defaultProjectName = calculateDefaultProjectName(
process.cwd(),
workspaceRoot,
readProjectsConfigurationFromProjectGraph(graph),
nxJson
);

const [project, target, config] = splitTarget(args.target, graph, {
currentProject: defaultProjectName,
});

if (project && target) {
return {
Expand All @@ -211,12 +220,7 @@ function resolveTargetIdentifier(
}

const targetName = project; // splitTarget returns the string as the first element
const projectName = calculateDefaultProjectName(
process.cwd(),
workspaceRoot,
readProjectsConfigurationFromProjectGraph(graph),
nxJson
);
const projectName = defaultProjectName;

if (!projectName) {
output.error({
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/daemon/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
PreTasksExecutionContext,
} from '../../project-graph/plugins/public-api';
import { preventRecursionInGraphConstruction } from '../../project-graph/project-graph';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration/source-maps';
import { isJsonMessage } from '../../utils/consume-messages-from-socket';
import { DelayedSpinner } from '../../utils/delayed-spinner';
import { isCI } from '../../utils/is-ci';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,8 @@ import { notifyFileChangeListeners } from './file-watching/file-change-events';
import { notifyProjectGraphListenerSockets } from './project-graph-listener-sockets';
import { serverLogger } from '../logger';
import { NxWorkspaceFilesExternals } from '../../native';
import {
ConfigurationResult,
ConfigurationSourceMaps,
} from '../../project-graph/utils/project-configuration-utils';
import { ConfigurationResult } from '../../project-graph/utils/project-configuration-utils';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration/source-maps';
import type { LoadedNxPlugin } from '../../project-graph/plugins/loaded-nx-plugin';
import {
DaemonProjectGraphError,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Socket } from 'net';
import { ProjectGraph } from '../../config/project-graph';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils';
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration/source-maps';
import { handleResult } from './server';
import { isV8SerializerEnabled } from '../is-v8-serializer-enabled';

Expand Down
8 changes: 3 additions & 5 deletions packages/nx/src/devkit-internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ export {
export { readNxJson as readNxJsonFromDisk } from './config/nx-json';
export { calculateDefaultProjectName } from './config/calculate-default-project-name';
export { retrieveProjectConfigurationsWithAngularProjects } from './project-graph/utils/retrieve-workspace-files';
export { mergeTargetConfigurations } from './project-graph/utils/project-configuration-utils';
export {
readProjectConfigurationsFromRootMap,
findMatchingConfigFiles,
} from './project-graph/utils/project-configuration-utils';
export { mergeTargetConfigurations } from './project-graph/utils/project-configuration/target-merging';
export { readProjectConfigurationsFromRootMap } from './project-graph/utils/project-configuration/project-nodes-manager';
export { findMatchingConfigFiles } from './project-graph/utils/project-configuration-utils';
export { getIgnoreObjectForTree } from './utils/ignore';
export { splitTarget } from './utils/split-target';
export { combineOptionsForExecutor } from './utils/params';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/executors/utils/convert-nx-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Observable } from 'rxjs';
import { readNxJson } from '../../config/nx-json';
import { Executor, ExecutorContext } from '../../config/misc-interfaces';
import { retrieveProjectConfigurations } from '../../project-graph/utils/retrieve-workspace-files';
import { readProjectConfigurationsFromRootMap } from '../../project-graph/utils/project-configuration-utils';
import { readProjectConfigurationsFromRootMap } from '../../project-graph/utils/project-configuration/project-nodes-manager';
import { ProjectsConfigurations } from '../../config/workspace-json-project-json';
import { getPlugins } from '../../project-graph/plugins/get-plugins';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import {
mergeProjectConfigurationIntoRootMap,
readProjectConfigurationsFromRootMap,
} from '../../project-graph/utils/project-configuration-utils';
} from '../../project-graph/utils/project-configuration/project-nodes-manager';
import { globWithWorkspaceContextSync } from '../../utils/workspace-context';
import { output } from '../../utils/output';
import { PackageJson } from '../../utils/package-json';
Expand Down
6 changes: 2 additions & 4 deletions packages/nx/src/project-graph/build-project-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@ import {
ProcessDependenciesError,
WorkspaceValidityError,
} from './error-types';
import {
ConfigurationSourceMaps,
mergeMetadata,
} from './utils/project-configuration-utils';
import { mergeMetadata } from './utils/project-configuration/target-merging';
import type { ConfigurationSourceMaps } from './utils/project-configuration/source-maps';
import { DelayedSpinner } from '../utils/delayed-spinner';
import { hashObject } from '../hasher/file-hasher';

Expand Down
6 changes: 2 additions & 4 deletions packages/nx/src/project-graph/error-types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {
ConfigurationResult,
ConfigurationSourceMaps,
} from './utils/project-configuration-utils';
import { ConfigurationResult } from './utils/project-configuration-utils';
import type { ConfigurationSourceMaps } from './utils/project-configuration/source-maps';
import { ProjectConfiguration } from '../config/workspace-json-project-json';
import { ProjectGraph } from '../config/project-graph';
import { CreateNodesFunctionV2 } from './plugins/public-api';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/project-graph/nx-deps-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
ProjectGraphErrorTypes,
StaleProjectGraphCacheError,
} from './error-types';
import { ConfigurationSourceMaps } from './utils/project-configuration-utils';
import { ConfigurationSourceMaps } from './utils/project-configuration/source-maps';

export interface FileMapCache {
version: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"results": [
[
[
"@acme/gradle",
"apps/my-app/build.gradle",
{
"projects": {
"apps/my-app": {
"name": ":apps:my-app",
"targets": {
"gradle-test": {
"cache": true,
"dependsOn": [
":apps:my-app:compileTestJava",
":apps:my-app:testClasses",
":apps:my-app:classes",
":apps:my-app:compileJava",
":libs:java:lib-a:jar",
":libs:java:lib-b:jar"
],
"executor": "@acme/gradle:gradle",
"options": {
"taskName": ":apps:my-app:test"
}
}
}
},
"libs/java/lib-a": {
"name": ":libs:java:lib-a",
"targets": {
"jar": {
"cache": true,
"executor": "@acme/gradle:gradle",
"options": {
"taskName": ":libs:java:lib-a:jar"
}
}
}
},
"libs/java/lib-b": {
"name": ":libs:java:lib-b",
"targets": {
"jar": {
"cache": true,
"executor": "@acme/gradle:gradle",
"options": {
"taskName": ":libs:java:lib-b:jar"
}
}
}
}
}
}
]
],
[
[
"nx/core/project-json",
"apps/my-app/project.json",
{
"projects": {
"apps/my-app": {
"name": "my-app",
"root": "apps/my-app",
"projectType": "application"
}
}
}
],
[
"nx/core/project-json",
"libs/java/lib-a/project.json",
{
"projects": {
"libs/java/lib-a": {
"name": "lib-a",
"root": "libs/java/lib-a"
}
}
}
],
[
"nx/core/project-json",
"libs/java/lib-b/project.json",
{
"projects": {
"libs/java/lib-b": {
"name": "lib-b",
"root": "libs/java/lib-b"
}
}
}
]
]
],
"nxJsonConfiguration": {},
"workspaceRoot": "/tmp/test-workspace",
"errors": []
}
Loading
Loading