Nx
Nx
Applications and libraries
https://nx.dev/more-concepts/applications-and-libraries
- a typical Nx workspace is structured into apps and libs
- Nx automatically creates TS path mappings in the
tsconfig.base.jsonfile, so that they can be easily consumed by other apps, e.g.:
// example of importing from another workspace library
import { Button } from "@flowershow/ui";
// ...
- library is not just a code put into a separate folder
- each library should have a "public API" exposed by its
index.tsfile - it forces developers into an "API thinking" (what should be exposed and what should be kept private)
- each library should have a "public API" exposed by its
- library doesn't have to be general purpose (consumed by multiple projects)
- it can be created just for better code organization
- ease of re-use can be a positive side effect though
- library doesn't have to be publishable
- library doesn't have to be buildable
- an app can consume it and build it itself directly
- it can be buildable though if we want to enable incremental builds
- libraries can be nested into sub-folders
- application can be thought of as a container that budles functionalities implemented in libraries
We could probably encapsulate some parts of the current template in libs (e.g. component's library, configs etc.). Not only so that in the future we can use them in other templates but also for the sake of separation of concerns, cleanliness, "API"
Recommended library types
https://nx.dev/more-concepts/library-types
Feature libraries
- "smart" components (with access to data sources), which implement a business use case or a page in an app
- almost always app-specific
- often lazy-loaded
UI libraries
- "dumb" components (presentational)
Data-access libraries
- code for interacting with back-end
- code related to state management
Utility libraries
- low level utilities used by many libraries and applications
- often no framework-specific code, e.g. pure functions
Should I make a new library?
- splitting code into libraries can make commands run faster
- faster
nx affected- e.g. if you split your one big library into a few smaller ones and run
nx affectedon a given target, it may be the case that this target doesn't have to be re-run on all the smaller libraries, because the changes you made only affect some part of the original mega-library
- e.g. if you split your one big library into a few smaller ones and run
- faster Nx computation caching
- i.e. faster commands's re-runs, faster saving to cache and fetching cached results
- faster
- it can also makes nx dependencies graph more valuable
- you can enforce constraints on how different types of libraries depend on each other using tags
Should I add code to an existing library?
Rule of thumb: if the code is closely related to some already existing library - probably yes.
- related code should be close together
- better DX as you don't need to jump around multiple different folders
- every new library adds some folders and config files, which do not directly contribute to business value
- enforcing constraints may not be beneficial for rapidly evolving code (like ours) as it can get in the way of experimentation and exploration
- if may be a good idea to develop a single library for a while so that the architecture starts to emerge naturally and refactor into smaller libraries when the pace of change has slowed down
Creating libraries
Nx makes it easy to add new libraries (or apps) to the monorepo, by using their code generators. Code generators automate repeatable tasks like scaffolding of similar projects and provide a standard for creating them.
For example, to create a new publishable, js node library:
nx g @nrwl/node:lib --js --publishable --importPath @flowershow/ui
https://nx.dev/plugin-features/use-code-generators
You can also create your own generators for more custom solutions.
Managing libraries
Move/rename:
nx g move --project booking-some-library shared/some-library
Remove:
nx g remove booking-some-library
Workspace
Structure
See the workspace structure exampleby Nx.
- libraries should be grouped by scope, which is usually an application they belong to
Tasks
- Target - the name of an action that can be taken on a project (e.g.,
build) - Task - an invocation of a target on a specific project (e.g.,
header:build)
Defining tasks
Tasks can be defined as npm scripts in a project's package.json file or as targets in a project.json file.
Example in project.json
{
"targets": {
https://nx.dev/plugin-features/use-code-generators "build": {
"executor": "nx:run-commands",
"options": {
"command": "webpack -c webpack.conf.js"
}
},
"test": {
"executor": "@nrwl/jest:jest",
"options": {
"codeCoverage": true
}
}
}
}
Task executors
- executors perform actions on your code (e.g. building, linting, testing)
How are they different from shell or npm scripts?
- they encourage a consistent methodology for performing similar actions across different projects
- nx can leverage this consistency to run the same target across multiple projects (e.g.
nx run-many --target=test) - executors provide metadata to define the available options, which allows the Nx CLI to show prompts in the terminal and Nx Console to generate a GUI for the executor
Running tasks
Run a single task:
npx nx test cli
Run a given target on all projects1:
npx nx run-many --target=build
Run tasks affected by the PR:
npx nx affected --target=test
Cache task results
Nx uses computation cache to never run the same task twice. It restores the results of running a given task from cache.
This is configured in nx.json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "test"]
}
}
}
}
To clear the cache run:
nx reset
Byy default it uses local computation cache. You can also use Nx Cloud distributed caching for storing the cache (free for open-source project).
To connect the workspace to Nx Cloud run:
npx nx connect-to-nx-cloud
Dependency graph
Exploring this graph visually can be useful to understand why Nx is behaving in a certain way and to get a high level view of your code architecture.
To launch the project graph visualization run:
nx graph
Code generators
- can be used to standardize and automate generation of libraries, apps, components, features etc.
Types:
- plugin generators - come from Nx plugins installed in the workspace
- local generators - custom generators created for the workspace
- update generators - are invoked by Nx plugins when you update Nx to keep your config files in sync with the latest versions of third party tools
Footnotes
-
Nx will figure out the right order in which to build projects. ↩