Fine-tuning Caching with Inputs
When Nx computes the hash for a given operation, it takes into account the inputs
of the target. The inputs
are a list of file sets, runtime inputs and environment variables that affect the output of the target. If any of the inputs
change, the cache is invalidated and the target is re-run.
To understand some of the examples below, let's imagine the following simple workspace.
Global vs per-project inputs
Tasks can have inputs
defined for them globally in the nx.json
file or on a per-project basis in the project configuration.
1{
2 "targetDefaults": {
3 "build": {
4 "inputs": ["..."]
5 }
6 }
7}
8
Include all project files and dependencies in cache hash
The following definition includes all files of the project itself as well as all of its dependencies in the cache hash, hence telling Nx to invalidate the cache whenever
- any file of the project itself changes
- any file of any dependency changes
1{
2 "namedInputs": {
3 "default": ["{projectRoot}/**/*"]
4 },
5 "targetDefaults": {
6 "build": {
7 "inputs": ["default", "^default"]
8 }
9 }
10}
11
This definition is the default behavior of Nx, even if you don't specify any inputs
at all. This is a rather cautious approach. It will always give you correct output, but it might re-run a task in some cases where the cache could have been used instead.
Note, Nx uses the minimatch library to process glob patterns.
ReplacementsNote how you can use {projectRoot}
and {workspaceRoot}
as placeholders to simplify writing glob definitions.
If you're wondering what namedInputs
are, read the next section.
Reusing inputs definitions with namedInputs
If you find yourself reusing the same inputs
definitions, you can instead create a namedInput
. It is like a variable definition which can then be reused in the inputs
array.
1{
2 "namedInputs": {
3 "default": ["{projectRoot}/**/*"]
4 },
5 "targetDefaults": {
6 "build": {
7 "inputs": ["default", "^default"]
8 }
9 }
10}
11
The ^
character at the beginning of the ^default
string means this entry applies to the project dependencies of the project, not the project itself. In our example myreactapp
depends on shared-ui
, so if we run
❯
nx build myreactapp
...then because we defined
"inputs": ["default",...]
- it will invalidate the cache ofmyreactapp
whenever some src file ofmyreactapp
itself changes"inputs": [..., "^default"]
- it will in addition invalidate the cache ofmyreactapp
whenever a file ofshared-ui
(or any of its dependencies) changes
Exclude files from invalidating cache
Sometimes you might want to exclude specific project files so that they don't invalidate the cache for a given target. For example, we want spec/test files to invalidate the test
target (by explicitly including *.spec.ts
files), but we might want to optimize and exclude them from our build
target. Hence, whenever we just change a test file, our build
cache would still be working.
Here's how we could define that, starting from our default situation:
1{
2 "namedInputs": {
3 "default": ["{projectRoot}/**/*"],
4 "production": ["default", "!{projectRoot}/**/?(*.)+spec.ts"]
5 },
6 "targetDefaults": {
7 "build": {
8 "inputs": ["production", "^production"]
9 }
10 }
11}
12
Note how we define a new production
named input which uses the default
named input and then excludes (note the !
) all spec related files. As a result, if a spec file changes either in myreactapp
or in shared-ui
, the build
target cache will not be invalidated.
Include environment variables in cache hash
You can also include environment variables in the cache hash key calculation in order to invalidate the cache if the environment variable value changes.
1{
2 "namedInputs": {
3 "default": ["{projectRoot}/**/*"]
4 },
5 "targetDefaults": {
6 "build": {
7 "inputs": [
8 "default",
9 "^default",
10
11 // this will include the value of API_KEY in the cache hash
12 { "env": "API_KEY" }
13 ]
14 }
15 }
16}
17
Include Runtime Information in the Cache Hash
Sometimes you might also want to consider runtime information as part of your hash. Assume you have a deploy target for the myreactapp
and assume we use Fly for it. We might want to make sure to just use the cache if the Fly version matches.
You can define it globally in the nx.json
but these targets are usually defined right where the project is, so here's an example of a package.json
and project.json
definition.
1{
2 "name": "myreactapp",
3 "dependencies": {},
4 "scripts": {
5 "deploy": "fly deploy"
6 },
7 ...
8 "nx": {
9 "targets": {
10 "deploy": {
11 "inputs": [
12 ...
13
14 // includes the value of running "fly version" in the cache hash
15 { "runtime": "fly version" }
16 ],
17 "dependsOn": ["build"],
18 }
19 }
20 }
21}
22
Include or Exclude External NPM dependencies from the Cache
You can also get more fine-grained when it comes to external dependencies. In our example of the Fly.io deployment, we don't really rely on any NPM packages for the deployment step, so we could ignore all of them. This can be done by using the externalDependencies
property.
1{
2 "name": "myreactapp",
3 "dependencies": {},
4 "scripts": {
5 "deploy": "fly deploy"
6 },
7 ...
8 "nx": {
9 "targets": {
10 "deploy": {
11 "inputs": [
12 ...,
13 // this explicitly tells the hasher to ignore all external packages when calculating the hash
14 { "externalDependencies": [] },
15
16 // includes the value of running "fly version" in the cache hash
17 { "runtime": "fly version" }
18 ],
19 "dependsOn": ["build"],
20 }
21 }
22 }
23}
24
By explicitly providing an empty array, we ignore all changes to external NPM packages. Similarly we could have another example, where we depend on just one specific NPM package. Like if we use Lerna for publishing, we can define it like this:
1{
2 "targets": {
3 "publish": {
4 "command": "lerna publish",
5 "inputs": [
6 "production",
7 // we explicitly say that our run-commands depends on lerna only
8 { "externalDependencies": ["lerna"] }
9 ],
10 "dependsOn": ["build"],
11 "cwd": "dist/{projectRoot}"
12 }
13 }
14}
15