Skip to content

Notes on nx configuration

lantua edited this page Nov 10, 2023 · 8 revisions

Task graph structure

All projects have a common dependency task structure: [build <= build-*] <= gen-file <= load-dm. The process of each task is as follows

  • load-dm pulls an external repo as necessary and puts the repo in an appropriate, fixed location,
  • gen-file creates files that are not committed, usually with a -gen infix in the name, usually using a repo loaded with load-dm,
  • build-* compiles the project using the generated file into a CommonJS artifact in dist/
  • build is a no-op task that links together each task.

We separate the compiling task into multiple build-* because each compiler requires a vastly different configuration. So we collect common configurations into build-vite, build-webpack, and build-tsc. Each project can then opt-in to an appropriate build-*. We then add build, which implicitly depends on all build-* so downstream projects can simply depend on build without knowing what compilation is used by upstream projects.

If project A depends on another project B, the build-* task of project A will depend on build task of project B. As such, build artifacts from project B in dist/ will be created before project A starts compilation. A sole exception to this rule is build-webpack (of project A), which only depends on gen-file (of project B). This is because Webpack uses explicitly the source code (of project B) directly, instead of the compilation artifact in dist/. It is currently unclear whether this applies to build-vite or build-tsc as well.

serve tasks are somewhat special, and does not depend on build. Instead, each uses its own buildTarget with appropriate target and configuration, generally <project>:build-webpack:<dev/prod> or <project>:build-vite:<dev/prod>, which in turn follows the dependency rules outlined above.

Pitfalls on nx configuration

This section includes, in no particular order, pitfalls for nx configuration, which may lead to a bug when configuring task graph. These behaviors may change with nx versions.

Multiple dependsOn fields override each other

If a task has multiple dependsOn values, one in nx.json, and another in the project's project.json, the value in project.json will override the value in nx.json. nx does NOT append the dependencies in the two lists. If the intended behaviour is to have the items in both lists, the value of dependsOn in project.json has to also include the entries in nx.json as well.

Task must be included, even with an empty configuration, to apply dependsOn transitively

Even with an empty configuration, a task, e.g., build, must be included in each project's project.json so that dependsOn field applies transitively with build task in the middle.

dependsOn field is not transitive across projects

I am unable to determine the exact rules for this, but even if build-* on project A depends on build on project B, and build on project B depends on gen-file on project B, it does NOT imply that build-* on project A depends on gen-file on project B. One has to include dependsOn: "^gen-file" to build-* on project A to "simulate" the transitive property appropriately.