Skip to content
Draft
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
57 changes: 19 additions & 38 deletions packages/nx/src/command-line/show/show-target/inputs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { TargetConfiguration } from '../../../config/workspace-json-project-json';
import type { HashInputs } from '../../../native';
import { workspaceRoot } from '../../../utils/workspace-root';
import { handleImport } from '../../../utils/handle-import';
import { createTaskFileResolver } from '../../../hasher/task-file-resolver';
import type { ShowTargetInputsOptions } from '../command-object';
import {
resolveTarget,
Expand All @@ -10,7 +9,6 @@ import {
hasCustomHasher,
pc,
printList,
type ResolvedTarget,
} from './utils';

// ── Handler ─────────────────────────────────────────────────────────
Expand All @@ -32,16 +30,29 @@ export async function showTargetInputsHandler(
return;
}

const hashInputs = await resolveInputFiles(t);
const { projectName, targetName, configuration } = t;
const taskId = configuration
? `${projectName}:${targetName}:${configuration}`
: `${projectName}:${targetName}`;

const resolver = await createTaskFileResolver({
projectGraph: t.graph,
nxJson: t.nxJson,
});

const hashInputs = resolver.getRawInputs(taskId);
if (!hashInputs) {
throw new Error(`Could not find hash plan for task "${taskId}".`);
}

if (args.check !== undefined) {
const checkItems = deduplicateFolderEntries(args.check);
const results = checkItems.map((input) =>
resolveCheckFromInputs(input, t.projectName, t.targetName, hashInputs)
resolveCheckFromInputs(input, projectName, targetName, hashInputs)
);

if (results.length >= 2) {
renderBatchCheckInputs(results, t.projectName, t.targetName);
renderBatchCheckInputs(results, projectName, targetName);
} else {
for (const data of results) renderCheckInput(data);
}
Expand All @@ -54,8 +65,8 @@ export async function showTargetInputsHandler(
}

renderInputs(
{ project: t.projectName, target: t.targetName, ...hashInputs },
t.node.data.targets[t.targetName].inputs,
{ project: projectName, target: targetName, ...hashInputs },
t.node.data.targets[targetName].inputs,
args
);
}
Expand All @@ -64,36 +75,6 @@ export async function showTargetInputsHandler(

type CheckInputResult = ReturnType<typeof resolveCheckFromInputs>;

async function resolveInputFiles(t: ResolvedTarget): Promise<HashInputs> {
const { projectName, targetName, configuration, graph, nxJson } = t;
const { HashPlanInspector } = (await handleImport(
'../../../hasher/hash-plan-inspector.js',
__dirname
)) as typeof import('../../../hasher/hash-plan-inspector');

const inspector = new HashPlanInspector(graph, workspaceRoot, nxJson);
await inspector.init();

const plan = inspector.inspectTaskInputs({
project: projectName,
target: targetName,
configuration,
});

const targetConfig = graph.nodes[projectName]?.data?.targets?.[targetName];
const effectiveConfig = configuration ?? targetConfig?.defaultConfiguration;
const taskId = effectiveConfig
? `${projectName}:${targetName}:${effectiveConfig}`
: `${projectName}:${targetName}`;
const result = plan[taskId];
if (!result) {
throw new Error(
`Could not find hash plan for task "${taskId}". Available tasks: ${Object.keys(plan).join(', ')}`
);
}
return result;
}

function resolveCheckFromInputs(
rawValue: string,
projectName: string,
Expand Down
55 changes: 31 additions & 24 deletions packages/nx/src/command-line/show/show-target/outputs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { getOutputsForTargetAndConfiguration } from '../../../tasks-runner/utils';
import {
createTaskFileResolver,
type TaskFileResolver,
} from '../../../hasher/task-file-resolver';
import { workspaceRoot } from '../../../utils/workspace-root';
import type { ShowTargetOutputsOptions } from '../command-object';
import {
Expand All @@ -16,12 +19,22 @@ export async function showTargetOutputsHandler(
args: ShowTargetOutputsOptions
): Promise<void> {
const t = await resolveTarget(args);
const outputsData = resolveOutputsData(t);

const taskId = t.configuration
? `${t.projectName}:${t.targetName}:${t.configuration}`
: `${t.projectName}:${t.targetName}`;

const resolver = await createTaskFileResolver({
projectGraph: t.graph,
nxJson: t.nxJson,
});

const outputsData = resolveOutputsData(t, resolver, taskId);

if (args.check !== undefined) {
const checkItems = deduplicateFolderEntries(args.check);
const results = checkItems.map((o) =>
resolveCheckOutputData(o, outputsData)
resolveCheckOutputData(o, outputsData, resolver, taskId)
);

if (results.length >= 2) {
Expand Down Expand Up @@ -49,13 +62,15 @@ export async function showTargetOutputsHandler(
type OutputsData = ReturnType<typeof resolveOutputsData>;
type CheckOutputResult = ReturnType<typeof resolveCheckOutputData>;

function resolveOutputsData(t: ResolvedTarget) {
function resolveOutputsData(
t: ResolvedTarget,
resolver: TaskFileResolver,
taskId: string
) {
const { projectName, targetName, configuration, node } = t;
const resolvedOutputs = getOutputsForTargetAndConfiguration(
{ project: projectName, target: targetName, configuration },
{},
node
);
// Use the resolver to obtain resolved output paths — avoids duplicating
// the getOutputsForTargetAndConfiguration call that the resolver already makes.
const resolvedOutputs = resolver.getOutputs(taskId);

const targetConfig = node.data.targets?.[targetName];
const configuredOutputs: string[] = targetConfig?.outputs ?? [];
Expand Down Expand Up @@ -93,25 +108,17 @@ function resolveOutputsData(t: ResolvedTarget) {

function resolveCheckOutputData(
rawFileToCheck: string,
outputsData: OutputsData
outputsData: OutputsData,
resolver: TaskFileResolver,
taskId: string
) {
const fileToCheck = normalizePath(rawFileToCheck);
const { outputPaths, expandedOutputs } = outputsData;

let matchedOutput: string | null = null;
for (const outputPath of outputPaths) {
const normalizedOutput = outputPath.replace(/\\/g, '/');
if (
fileToCheck === normalizedOutput ||
fileToCheck.startsWith(normalizedOutput + '/')
) {
matchedOutput = outputPath;
break;
}
}
if (!matchedOutput && expandedOutputs.includes(fileToCheck)) {
matchedOutput = fileToCheck;
}
// Delegate the direct-match decision to the resolver (handles exact, prefix,
// and glob matching via minimatch — supersedes the previous manual prefix
// comparison + expandedOutputs exact-match approach).
const matchedOutput = resolver.isOutput(taskId, fileToCheck);

let containedOutputPaths: string[] = [];
let containedExpandedOutputs: string[] = [];
Expand Down
3 changes: 3 additions & 0 deletions packages/nx/src/devkit-internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ export { globalSpinner } from './utils/spinner';
export { signalToCode } from './utils/exit-codes';
export { handleImport } from './utils/handle-import';
export { PluginCache, safeWriteFileCache } from './utils/plugin-cache-utils';
export { HashPlanInspector } from './hasher/hash-plan-inspector';
export type { TaskFileResolver } from './hasher/task-file-resolver';
export { createTaskFileResolver } from './hasher/task-file-resolver';
Loading
Loading