Customizing Inputs and Named Inputs
The inputs
property of a task allows you to define under what conditions the cache for that task should be invalidated and the task should be run again. Tasks can have inputs
defined for them globally in the nx.json
file or on a per-project basis in the project configuration. Those inputs
can be fileset
s, runtime
inputs or env
variables.
If you find yourself reusing the same inputs
definitions, you can instead create a namedInput
in the project configuration or nx.json
and use that namedInput
in your inputs
array.
Knowing the syntax doesn't always explain how you would use the feature, so here are two scenarios explaining how to customize inputs
to match your particular set up.
Defaults
If you don't specify any inputs
, Nx uses as inputs every file in the task's project and every file in that project's dependencies. It's the same as writing this:
{
"namedInputs": {
"default": ["{projectRoot}/**/*"]
},
"targetDefaults": {
"build": {
"inputs": ["default", "^default"]
}
}
}
This default behavior works and will always give you correct output, but it might re-run a task in some cases where the cache could have been used instead. Modifying inputs
and namedInputs
is all about getting the most you can out of the caching mechanism.
Scenario 1: React App
Let's say we have a repo with two projects. There's a React app (react-app
) that depends on a React UI library (ui-library
).
Global Settings
In the root nx.json
file, we can set the following namedInputs
:
{
"namedInputs": {
"sharedGlobals": ["{workspaceRoot}/babel.config.json"],
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": [
"default",
"!{projectRoot}/**/*.spec.ts",
"!{projectRoot}/**/*.md",
"!{projectRoot}/tsconfig.spec.json",
"!{projectRoot}/jest.config.ts",
"!{projectRoot}/.eslintrc.json"
]
}
}
This configuration defines three namedInputs
.
sharedGlobals
contains root level files that should invalidate the cache for all projects. Note thattsconfig.base.json
and thepackage.json
lock files are handled behind the scenes by Nx so that when you modify the specific properties that affect your project, the cache is invalidated.default
contains every file in the project and everything defined insharedGlobals
production
starts with every file indefault
, but removes.spec.ts
files, markdown files and some project-specific config files.
These names are not hard-coded. You can add your own namedInputs
or rename these as you see fit.
With these namedInputs
in place, we can set default inputs
for project targets in nx.json
:
{
"namedInputs": {
"sharedGlobals": [
/* ... */
],
"default": [
/* ... */
],
"production": [
/* ... */
]
},
"targetDefaults": {
"build": {
"inputs": ["production", "^production"],
"dependsOn": ["^build"]
},
"test": {
"inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"]
}
}
}
In this configuration, build
needs to be re-run if the project's production
files are changed or the dependencies' production
files are changed. The "dependsOn": ["^build"]
setting ensures that the output files of the dependencies are correct, but it does not tell Nx to re-run this project's build target when those outputs change. That's why "^production"
is needed in the inputs
array.
Note that we are using ^production
instead of ^default
because changing test files of a dependency should not invalidate the build cache for this project.
The test
target cache is invalidated if test or production code is changed in this project (default
) or if production code is changed in a dependency ^production
. The tests also need to be run again if the root level jest.preset.js
file is modified.
Project Settings
In the ui-library
library, we have some *.spec.tsx
files that we don't want to count as production files. We can modify the project configuration to account for this:
~/workspace❯
ui-library/project.json
{
"namedInputs": {
"production": [
// copied this definition from nx.json
"default",
"!{projectRoot}/**/*.spec.ts",
"!{projectRoot}/**/*.spec.tsx", // added this line
"!{projectRoot}/**/*.md",
"!{projectRoot}/tsconfig.spec.json",
"!{projectRoot}/jest.config.ts",
"!{projectRoot}/.eslintrc.json"
]
}
}
Note that I need to copy the entire definition of production
from nx.json
. This entry overwrites the existing production
entry defined in nx.json
, but default
and sharedGlobals
are inherited. Now when I modify a spec.tsx
file in ui-library
, the build for ui-library
is unaffected and the build for react-app
is unaffected.
You could also handle this scenario by modifying the production
definition in the nx.json
so that it applies to all projects in the repo. In fact, the defaults created by Nx do exactly that: "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)"
. This is a complex glob expression though and is not particularly helpful for explaining the concepts of inputs
and namedInputs
.
The build
and test
inputs
do not need to be modified at the project level, since the defaults defined at the root are still correct.
Scenario 2: Generated Documentation Site
Let's say you have a repo with two projects, a docs-site
app that contains all the code for rendering a documentation site and a content
library that contains markdown files that are used as the content of the site.
The content
library has a node script .ts
file that is used to check through the markdown files for internal broken links. The test
target looks like this:
{
"targets": {
"test": {
"inputs": ["default", "^production"],
"executor": "nx:run-commands",
"command": "ts-node ./check-for-broken-links.ts"
}
}
}
Note that we are overwriting the default inputs
for test
because changes to the root jest.preset.js
file shouldn't affect this project's tests.
This library's production
definition should look like this:
{
"namedInputs": {
"production": ["default", "!{projectRoot}/**/*.ts"]
}
}
This ensures that the .md
files are included in the production
file set, but the check-for-broken-links.ts
script is not. Now changes to a markdown file in content
will force a re-build of docs-site
, even though changes to markdown files in docs-site
will not force a re-build.