Skip to content

{projectRoot} not interpolated inside {workspaceRoot} input patterns in native hasher #34595

@Hoffs

Description

@Hoffs

Current Behavior

When using a {workspaceRoot}/{projectRoot}/**/*.go pattern in target inputs, the {projectRoot} token is not interpolated by the native Rust hash planner. The pattern silently matches zero files, causing the cache hash to exclude those files entirely.

// nx.json
"namedInputs": {
  "gosourceUnfiltered": ["{workspaceRoot}/{projectRoot}/**/*.go"]
}
// target config
"format": {
  "inputs": ["gosourceUnfiltered", { "externalDependencies": [] }]
}

The hash plan for this target contains zero .go file inputs:

Task: my-project:format:write
  Inputs:
    my-project:ProjectConfiguration
    my-project:TsConfig
    env:NX_CLOUD_ENCRYPTION_KEY
    file:nx.json
    file:.gitignore
    // no .go files!

Root Cause

In the Rust hash planner (hash_planner.rs), fileset inputs are partitioned based on their prefix:

.partition(|file_set| {
    file_set.starts_with("{projectRoot}/") || file_set.starts_with("!{projectRoot}/")
});

A pattern starting with {workspaceRoot}/ is classified as a workspace fileset. It then flows to hash_workspace_files.rs where {workspaceRoot}/ is stripped via strip_prefix("{workspaceRoot}/"), leaving {projectRoot}/**/*.go. But {projectRoot} is never substituted with the actual project root, so the glob literally tries to match paths starting with {projectRoot}/ — which don't exist.

Expected Behavior

Per the Nx docs on inputs:

{workspaceRoot} should only appear in the beginning of an input but {projectRoot} and {projectName} can be specified later in the input to interpolate the root or name of the project into the input location.

The native hasher should interpolate {projectRoot} (and {projectName}) within {workspaceRoot} patterns before glob matching. After stripping {workspaceRoot}/ and interpolating {projectRoot}, the pattern should become e.g. packages/shared/go/middleware/**/*.go and correctly match files.

Impact

This is a silent cache correctness issue: targets using this pattern appear to work but their cache hash doesn't include the matched files. Changes to those files won't invalidate the cache. There's no warning or error emitted.

Workaround

Use the literal path instead of {projectRoot} inside workspace-level patterns:

// In a createNodes plugin, instead of:
inputs: ["{workspaceRoot}/{projectRoot}/**/*.go"]
// Use:
inputs: [`{workspaceRoot}/${actualProjectRoot}/**/*.go`]

Related

Environment

  • Nx version: 22.6.0-beta.3
  • OS: macOS (Darwin 24.6.0)
  • Package manager: pnpm

Metadata

Metadata

Labels

priority: mediumMedium Priority (not high, not low priority)scope: corecore nx functionality

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions