# Using Nx to Build Projects Bitwarden uses [Nx](https://nx.dev/) to make building projects from the monorepo easier. To build, lint, or test a project you'll want to reference the project's `project.json` file for availible commands and their names. Then you'll run `npx nx [your_command] [your_project] [your_options]`. Run `npx nx --help` to see availible options, there are many. Please note: the Nx implementation is a work in progress. CI still uses the old npm builds, and we have many "legacy" libraries that use hacks to get them into the Nx project graph. ## Quick Start ### Basic Commands ```bash # Build a project npx nx build cli npx nx build-native desktop # Some apps have special build commands npx nx build state # Modern libs and apps have simple, all lowercase target names npx nx build @bitwarden/common # Legacy libs have a special naming convention and include the @bitwarden prefix # Test a project npx nx test cli # Lint a project npx nx lint cli # Serve/watch a project (for projects with serve targets) npx nx serve cli # Build all projects that differ from origin/main nx affected --target=build --base=origin/main # Build, lint, and test every project at once npx nx run-many --target=build,test,lint --all # Most projects default to the "oss-dev" build, so if you need the bitwarden license build add a --configuration npx nx build cli --configuration=commercial-dev # If you need a production build drop the "dev" suffix npx nx build cli --configuration=oss # or "commercial" # Configurations can also be passed to run-many # For example: to run all Bitwarden licensed builds npx nx run-many --target=build,test,lint --all --configuration=commercial # Outputs are distrubuted in a root level /dist/ folder # Run a locally built CLI node dist/apps/cli/oss-dev/bw.js ``` ### Global Commands ```bash # See all projects npx nx show projects # Run affected projects only (great for local dev and CI) npx nx affected:build npx nx affected:test npx nx affected:lint # Show dependency graph npx nx dep-graph ``` ## Library Projects Our libraries use two different Nx integration patterns depending on their migration status. ### Legacy Libraries Most existing libraries use a facade pattern where `project.json` delegates to existing npm scripts. This approach maintains backward compatibility with the build methods we used before introducing Nx. These libraries are considered tech debt and Platform has a focus on updating them. For an example reference `libs/common/project.json`. These libraries use `nx:run-script` executor to call existing npm scripts: ```json { "targets": { "build": { "executor": "nx:run-script", "options": { "script": "build" } } } } ``` #### Available Commands for Legacy Libraries All legacy libraries support these standardized commands: - **`nx build `** - Build the library - **`nx build:watch `** - Build and watch for changes - **`nx clean `** - Clean build artifacts - **`nx test `** - Run tests - **`nx lint `** - Run linting ### Modern Libraries Newer libraries like `libs/state` use native Nx executors for better performance and caching. ```json { "targets": { "build": { "executor": "@nx/js:tsc", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/libs/state" } } } } ``` ## What Happens When You Run An Nx Command ```mermaid flowchart TD Start([You just ran an nx command]) --> ParseCmd[Nx parses command args] ParseCmd --> ReadWorkspace[Nx reads nx.json, workspace configuration, cache settings, and plugins] ReadWorkspace --> ReadProject[Nx reads project.json, finds the target configuration, and checks executor to use] ReadProject --> CheckCache{Nx checks the cache: has this exact build been done before?} CheckCache -->|Cache hit| UseCached[Nx uses cached outputs, copies from .nx/cache, and skips execution] UseCached --> Done([Your command is done]) CheckCache -->|Cache miss| DetermineExecutor{Which executor is configured?} DetermineExecutor -->|nx:run-script| FacadePattern[Legacy Facade Pattern] DetermineExecutor -->|nx/webpack:webpack| WebpackExecutor[Webpack Executor] DetermineExecutor -->|nx/js:tsc| TypeScriptExecutor[TypeScript Executor] DetermineExecutor -->|nx/jest:jest| JestExecutor[Jest Executor] DetermineExecutor -->|nx/eslint:lint| ESLintExecutor[ESLint Executor] %% Facade Pattern Flow FacadePattern --> ReadPackageJson[The run-script executor finds npm script to run in package.json] ReadPackageJson --> RunNpmScript[Npm script is executed] RunNpmScript --> NpmDelegates{What does the npm script do?} NpmDelegates -->|TypeScript| TSCompile[TypeScript compiles to JavaScript using tsconfig.json] NpmDelegates -->|Webpack| WebpackBuild[Webpack bundles and optimizes code] NpmDelegates -->|Jest| JestTest[Jest executes unit tests] TSCompile --> FacadeOutput[Outputs written to libs/LIB/dist/] WebpackBuild --> FacadeOutput JestTest --> FacadeOutput FacadeOutput --> CacheResults1[Nx caches results in .nx/cache/] %% Webpack Executor Flow WebpackExecutor --> ReadWebpackConfig[Webpack config read from apps/cli/webpack.config.js or bit-cli/webpack.config.js] ReadWebpackConfig --> ConfigureWebpack[Webpack configured with entry points, TypeScript paths, and plugins] ConfigureWebpack --> WebpackProcess[Webpack resolves paths, compiles TypeScript, bundles dependencies, and applies optimizations] WebpackProcess --> WebpackOutput[Single executable bundle written to dist/apps/cli/] WebpackOutput --> CacheResults2[Nx caches results in .nx/cache/] %% TypeScript Executor Flow TypeScriptExecutor --> ReadTSConfig[TypeScript reads tsconfig.lib.json compilation options] ReadTSConfig --> TSProcess[TypeScript performs type checking, emits declarations, and compiles to JavaScript] TSProcess --> TSOutput[Outputs written to dist/libs/LIB/] TSOutput --> CacheResults3[Nx caches results in .nx/cache/] %% Jest Executor Flow JestExecutor --> ReadJestConfig[Jest reads jest.config.js test configuration] ReadJestConfig --> JestProcess[Jest finds test files, runs suites, and generates coverage] JestProcess --> JestOutput[Test results and coverage reports output] JestOutput --> CacheResults4[Nx caches results in .nx/cache/] %% ESLint Executor Flow ESLintExecutor --> ReadESLintConfig[ESLint reads .eslintrc.json rules and configuration] ReadESLintConfig --> ESLintProcess[ESLint checks code style, finds issues, and applies auto-fixes] ESLintProcess --> ESLintOutput[Lint results with errors and warnings output] ESLintOutput --> CacheResults5[Nx caches results in .nx/cache/] %% All paths converge CacheResults1 --> UpdateGraph[Dependency graph updated to track project relationships] CacheResults2 --> UpdateGraph CacheResults3 --> UpdateGraph CacheResults4 --> UpdateGraph CacheResults5 --> UpdateGraph UpdateGraph --> Done ``` ## Caching and Performance ### Nx Caching Nx automatically caches build outputs and only rebuilds what changed: ```bash # First run builds everything npx nx build cli # Second run uses cache (much faster) npx nx build cli ``` ### Clearing Cache ```bash # Clear all caches npx nx reset ``` ## Additional Resources - [Nx Intro Documentation](https://nx.dev/getting-started/intro) - [Nx CLI Commands Reference](https://nx.dev/docs/reference/nx-commands) - [Nx Workspace Configuration](https://nx.dev/reference/project-configuration)