From 3386fadb22ae488a1ccb721c9d66eae3a838f35d Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Tue, 27 May 2025 21:45:59 -0400 Subject: [PATCH 01/10] initial draft of nx.md --- docs/contributing/nx.md | 145 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 docs/contributing/nx.md diff --git a/docs/contributing/nx.md b/docs/contributing/nx.md new file mode 100644 index 00000000..17204dc6 --- /dev/null +++ b/docs/contributing/nx.md @@ -0,0 +1,145 @@ +# Nx at Bitwarden + +## What is Nx? + +Nx is a powerful open-source build system designed specifically for monorepo development. It provides tools and techniques for enhancing developer productivity, optimizing CI performance, and maintaining code quality in complex JavaScript/TypeScript codebases that contain multiple applications and libraries within a single repository. + +## Why We're Using Nx + +We use Nx in the Bitwarden clients monorepo to improve our development workflow and build efficiency. Key advantages include: + +### 1. Unified Commands +Instead of navigating to specific directories and running individual build commands, you can now execute commands for any project from the repository root: + +```bash +# Old way +cd apps/web +npm run build +``` + +```bash +# New way with Nx +nx build web +nx serve desktop +nx test common +``` + +### 2. Build Caching +Nx automatically caches build results. If code and dependencies haven't changed, subsequent builds are significantly faster, often restoring results from cache instantly. This saves considerable build time and memory. + +### 3. Intelligent Dependency Management +Nx understands the dependencies between projects. Running a build for one project automatically builds its dependencies first if needed: + +You can visualize these dependencies with: +```bash +nx graph +``` + +### 4. Configuration-Based Projects +Each app and library can be defined as a project with a =project.json= file that specifies its build configurations, targets, and other settings. This replaces many scripts previously defined in individual =package.json= files. + +## Key Nx Terminology + +- *nx.json*: The primary configuration file for the Nx workspace, located at the root. Defines global settings, plugins, workspace layout, and target defaults. + +- *project.json*: A configuration file in each project's directory that defines the targets (tasks) for that specific project and the executors used to run them. + +- *Target*: A specific task that can be performed on a project, like =build=, =serve=, =lint=, or =test=. Run with =nx =. + +- *Executor*: The code responsible for performing a target's action, typically provided by Nx plugins (e.g., =@nx/webpack:webpack= for running Webpack builds). + +- *Configuration*: A named set of options for running a target in different modes, accessed via the =--configuration= flag (e.g., =nx build browser --configuration=chrome-mv3=). + +## Using Nx + +To use the Nx cli you have two options: + +1. Install nx globally +2. Use npx + +## Common Commands + +### Building Projects + +```bash +# Build a project with default configuration +nx build + +# Build with specific configuration +nx build browser --configuration=chrome-mv3 +nx build web --configuration=bit + +# Build all projects (rarely needed) +nx run-many --target=build --all +``` + +### Serving Projects for Development + +```bash +# Start development server +nx serve + +# Serve with specific configuration +nx serve browser --configuration=firefox-mv2-dev +``` + +### Testing + +```bash +# Run tests for a project +nx test + +# Run tests only for projects affected by your changes +nx affected --target=test +``` + +### Other Useful Commands + +```bash +# Analyze dependencies between projects +nx graph + +# Run a task for all projects affected by your changes +nx affected --target= + +# Show what commands would be run, but don't execute them +nx affected --target=build --dry-run +``` + +## Understanding the Cache + +Nx stores its cache in the =.nx/cache= directory at the root of the repository. This includes: + +- Terminal outputs +- Build artifacts +- Metadata about the task execution + +The cache is created based on the inputs defined for each target. If none of the inputs have changed, Nx will restore the previous output. + +## Contributing Guidelines + +When contributing to Bitwarden with Nx: + +1. *Use Nx commands* from the repository root instead of navigating to individual project directories. +2. *Respect project boundaries* - imports between projects should follow the established dependency graph. +3. *When adding new dependencies* between projects, ensure they're reflected in imports in the code. +4. *For new scripts or build steps*, add them to the appropriate project's =project.json= file rather than to individual =package.json= files. +5. *Test affected projects* before submitting a PR: + ```bash + nx affected --target=test + nx affected --target=lint + `` + +## Getting Help + +If you're having issues with Nx: + +1. Check the [Nx documentation](https://nx.dev/getting-started/intro) +2. Run =nx --help= or =nx --help= for command-specific help +3. Reach out to the Platform team for assistance with Nx-specific problems + +## References + +- [Nx Documentation](https://nx.dev/getting-started/intro) +- [Nx Project Configuration Reference](https://nx.dev/reference/project-configuration) +- [Nx Cache Documentation](https://nx.dev/concepts/how-caching-works) From 8d58af73faed8c544c93123d5b57fd5c2120c6e2 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 28 May 2025 10:38:48 -0400 Subject: [PATCH 02/10] creating a lib --- docs/contributing/creating-a-lib.md | 82 +++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 docs/contributing/creating-a-lib.md diff --git a/docs/contributing/creating-a-lib.md b/docs/contributing/creating-a-lib.md new file mode 100644 index 00000000..c8448bd9 --- /dev/null +++ b/docs/contributing/creating-a-lib.md @@ -0,0 +1,82 @@ +# Creating a New Library + +This guide explains how to create a new library in the Bitwarden monorepo using Nx. + +## Prerequisites + +- Node.js and npm installed +- Bitwarden monorepo cloned and dependencies installed +- The Nx cli installed, or accessible over npx + +## Creating a Library with Nx + +We use the `@nx/js` plugin to generate new libraries. Follow these steps to create a new library: + +1. From the root of the monorepo, run the generator command: + + nx g @nx/js:lib my-new-lib --directory=libs/my-new-lib + + Replace `my-new-lib` with the name of your library. + +2. The generator will ask you questions about the library configuration. Here are recommended settings: + + - **Which bundler would you like to use?** Select `none` (we use our own bundling setup) + - **What should be the project name?** The same name as your library (e.g., `my-new-lib`) + - **Would you like to add a package.json?** Select `Yes` + - **Which unit test runner would you like to use?** Select `jest` (our standard test runner) + +3. Once generated, the library will have this structure: + +``` +libs/ + my-new-lib/ + src/ + index.ts + my-new-lib.ts + tsconfig.json + project.json + package.json + jest.config.js +``` + +4. Update the `package.json` with the appropriate Bitwarden naming convention: + +```json +{ + "name": "@bitwarden/my-new-lib", + "version": "0.0.0" +} +``` + +5. Update the library's path in the root `tsconfig.base.json` file to make it available to other projects: + +```json +{ + "compilerOptions": { + "paths": { + "@bitwarden/my-new-lib": ["libs/my-new-lib/src"], + } + } +} +``` + +## Building and Testing the Library + +- Build the library: `nx build my-new-lib` +- Test the library: `nx test my-new-lib` +- Lint the library: `nx lint my-new-lib` + +## Importing Your Library in Other Projects + +Once your library is created and built, you can import it in other projects within the monorepo: + +```ts +import { someFunction } from "@bitwarden/my-new-lib"; +``` + +## Best Practices + +- Follow the Bitwarden code style guidelines +- Document your library's public API +- Write unit tests for your library's functionality +- If your library has dependencies on other libraries, make sure to add them to the `project.json` file From bfa658a9423c89e51996f0c85475b1cd5ec71503 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 28 May 2025 10:43:27 -0400 Subject: [PATCH 03/10] fix a src block --- docs/contributing/creating-a-lib.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/contributing/creating-a-lib.md b/docs/contributing/creating-a-lib.md index c8448bd9..3de2fa39 100644 --- a/docs/contributing/creating-a-lib.md +++ b/docs/contributing/creating-a-lib.md @@ -13,13 +13,12 @@ This guide explains how to create a new library in the Bitwarden monorepo using We use the `@nx/js` plugin to generate new libraries. Follow these steps to create a new library: 1. From the root of the monorepo, run the generator command: - + ```bash nx g @nx/js:lib my-new-lib --directory=libs/my-new-lib - + ``` Replace `my-new-lib` with the name of your library. 2. The generator will ask you questions about the library configuration. Here are recommended settings: - - **Which bundler would you like to use?** Select `none` (we use our own bundling setup) - **What should be the project name?** The same name as your library (e.g., `my-new-lib`) - **Would you like to add a package.json?** Select `Yes` From 9526af4a098e3ea81edbe02313e21087f7a5c717 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 28 May 2025 12:19:54 -0400 Subject: [PATCH 04/10] fix a src block --- docs/contributing/nx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/nx.md b/docs/contributing/nx.md index 17204dc6..7a37ee4c 100644 --- a/docs/contributing/nx.md +++ b/docs/contributing/nx.md @@ -128,7 +128,7 @@ When contributing to Bitwarden with Nx: ```bash nx affected --target=test nx affected --target=lint - `` + ``` ## Getting Help From 41552fc8b275b8d78177fa4c198e70a61f20fcc1 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 28 May 2025 12:46:05 -0400 Subject: [PATCH 05/10] convert a bunch of org syntax to md --- docs/contributing/nx.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/contributing/nx.md b/docs/contributing/nx.md index 7a37ee4c..b764b938 100644 --- a/docs/contributing/nx.md +++ b/docs/contributing/nx.md @@ -5,7 +5,7 @@ Nx is a powerful open-source build system designed specifically for monorepo development. It provides tools and techniques for enhancing developer productivity, optimizing CI performance, and maintaining code quality in complex JavaScript/TypeScript codebases that contain multiple applications and libraries within a single repository. ## Why We're Using Nx - + We use Nx in the Bitwarden clients monorepo to improve our development workflow and build efficiency. Key advantages include: ### 1. Unified Commands @@ -36,7 +36,7 @@ nx graph ``` ### 4. Configuration-Based Projects -Each app and library can be defined as a project with a =project.json= file that specifies its build configurations, targets, and other settings. This replaces many scripts previously defined in individual =package.json= files. +Each app and library can be defined as a project with a `project.json` file that specifies its build configurations, targets, and other settings. This replaces many scripts previously defined in individual `package.json` files. ## Key Nx Terminology @@ -44,11 +44,11 @@ Each app and library can be defined as a project with a =project.json= file that - *project.json*: A configuration file in each project's directory that defines the targets (tasks) for that specific project and the executors used to run them. -- *Target*: A specific task that can be performed on a project, like =build=, =serve=, =lint=, or =test=. Run with =nx =. +- *Target*: A specific task that can be performed on a project, like `build`, `serve`, `lint`, or `test`. Run with `nx `. -- *Executor*: The code responsible for performing a target's action, typically provided by Nx plugins (e.g., =@nx/webpack:webpack= for running Webpack builds). +- *Executor*: The code responsible for performing a target's action, typically provided by Nx plugins (e.g., `@nx/webpack:webpack` for running Webpack builds). -- *Configuration*: A named set of options for running a target in different modes, accessed via the =--configuration= flag (e.g., =nx build browser --configuration=chrome-mv3=). +- *Configuration*: A named set of options for running a target in different modes, accessed via the `--configuration` flag (e.g., `nx build browser --configuration=chrome-mv3`). ## Using Nx @@ -108,7 +108,7 @@ nx affected --target=build --dry-run ## Understanding the Cache -Nx stores its cache in the =.nx/cache= directory at the root of the repository. This includes: +Nx stores its cache in the `.nx/cache` directory at the root of the repository. This includes: - Terminal outputs - Build artifacts @@ -123,7 +123,7 @@ When contributing to Bitwarden with Nx: 1. *Use Nx commands* from the repository root instead of navigating to individual project directories. 2. *Respect project boundaries* - imports between projects should follow the established dependency graph. 3. *When adding new dependencies* between projects, ensure they're reflected in imports in the code. -4. *For new scripts or build steps*, add them to the appropriate project's =project.json= file rather than to individual =package.json= files. +4. *For new scripts or build steps*, add them to the appropriate project's `project.json` file rather than to individual `package.json` files. 5. *Test affected projects* before submitting a PR: ```bash nx affected --target=test @@ -135,7 +135,7 @@ When contributing to Bitwarden with Nx: If you're having issues with Nx: 1. Check the [Nx documentation](https://nx.dev/getting-started/intro) -2. Run =nx --help= or =nx --help= for command-specific help +2. Run `nx --help` or `nx --help` for command-specific help 3. Reach out to the Platform team for assistance with Nx-specific problems ## References From 43dc8b550638494c2bd559d4cb283457dab38e6a Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 28 May 2025 12:48:35 -0400 Subject: [PATCH 06/10] ran prettier --- docs/contributing/creating-a-lib.md | 18 ++++++--- docs/contributing/nx.md | 62 ++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/docs/contributing/creating-a-lib.md b/docs/contributing/creating-a-lib.md index 3de2fa39..b5317f15 100644 --- a/docs/contributing/creating-a-lib.md +++ b/docs/contributing/creating-a-lib.md @@ -1,7 +1,7 @@ # Creating a New Library This guide explains how to create a new library in the Bitwarden monorepo using Nx. - + ## Prerequisites - Node.js and npm installed @@ -13,15 +13,19 @@ This guide explains how to create a new library in the Bitwarden monorepo using We use the `@nx/js` plugin to generate new libraries. Follow these steps to create a new library: 1. From the root of the monorepo, run the generator command: + ```bash nx g @nx/js:lib my-new-lib --directory=libs/my-new-lib ``` + Replace `my-new-lib` with the name of your library. -2. The generator will ask you questions about the library configuration. Here are recommended settings: +2. The generator will ask you questions about the library configuration. Here are recommended + settings: + - **Which bundler would you like to use?** Select `none` (we use our own bundling setup) - **What should be the project name?** The same name as your library (e.g., `my-new-lib`) - - **Would you like to add a package.json?** Select `Yes` + - **Would you like to add a package.json?** Select `Yes` - **Which unit test runner would you like to use?** Select `jest` (our standard test runner) 3. Once generated, the library will have this structure: @@ -47,13 +51,14 @@ libs/ } ``` -5. Update the library's path in the root `tsconfig.base.json` file to make it available to other projects: +5. Update the library's path in the root `tsconfig.base.json` file to make it available to other + projects: ```json { "compilerOptions": { "paths": { - "@bitwarden/my-new-lib": ["libs/my-new-lib/src"], + "@bitwarden/my-new-lib": ["libs/my-new-lib/src"] } } } @@ -78,4 +83,5 @@ import { someFunction } from "@bitwarden/my-new-lib"; - Follow the Bitwarden code style guidelines - Document your library's public API - Write unit tests for your library's functionality -- If your library has dependencies on other libraries, make sure to add them to the `project.json` file +- If your library has dependencies on other libraries, make sure to add them to the `project.json` + file diff --git a/docs/contributing/nx.md b/docs/contributing/nx.md index b764b938..10771858 100644 --- a/docs/contributing/nx.md +++ b/docs/contributing/nx.md @@ -2,14 +2,20 @@ ## What is Nx? -Nx is a powerful open-source build system designed specifically for monorepo development. It provides tools and techniques for enhancing developer productivity, optimizing CI performance, and maintaining code quality in complex JavaScript/TypeScript codebases that contain multiple applications and libraries within a single repository. +Nx is a powerful open-source build system designed specifically for monorepo development. It +provides tools and techniques for enhancing developer productivity, optimizing CI performance, and +maintaining code quality in complex JavaScript/TypeScript codebases that contain multiple +applications and libraries within a single repository. ## Why We're Using Nx - -We use Nx in the Bitwarden clients monorepo to improve our development workflow and build efficiency. Key advantages include: + +We use Nx in the Bitwarden clients monorepo to improve our development workflow and build +efficiency. Key advantages include: ### 1. Unified Commands -Instead of navigating to specific directories and running individual build commands, you can now execute commands for any project from the repository root: + +Instead of navigating to specific directories and running individual build commands, you can now +execute commands for any project from the repository root: ```bash # Old way @@ -25,36 +31,50 @@ nx test common ``` ### 2. Build Caching -Nx automatically caches build results. If code and dependencies haven't changed, subsequent builds are significantly faster, often restoring results from cache instantly. This saves considerable build time and memory. + +Nx automatically caches build results. If code and dependencies haven't changed, subsequent builds +are significantly faster, often restoring results from cache instantly. This saves considerable +build time and memory. ### 3. Intelligent Dependency Management -Nx understands the dependencies between projects. Running a build for one project automatically builds its dependencies first if needed: + +Nx understands the dependencies between projects. Running a build for one project automatically +builds its dependencies first if needed: You can visualize these dependencies with: + ```bash nx graph ``` ### 4. Configuration-Based Projects -Each app and library can be defined as a project with a `project.json` file that specifies its build configurations, targets, and other settings. This replaces many scripts previously defined in individual `package.json` files. + +Each app and library can be defined as a project with a `project.json` file that specifies its build +configurations, targets, and other settings. This replaces many scripts previously defined in +individual `package.json` files. ## Key Nx Terminology -- *nx.json*: The primary configuration file for the Nx workspace, located at the root. Defines global settings, plugins, workspace layout, and target defaults. +- _nx.json_: The primary configuration file for the Nx workspace, located at the root. Defines + global settings, plugins, workspace layout, and target defaults. -- *project.json*: A configuration file in each project's directory that defines the targets (tasks) for that specific project and the executors used to run them. +- _project.json_: A configuration file in each project's directory that defines the targets (tasks) + for that specific project and the executors used to run them. -- *Target*: A specific task that can be performed on a project, like `build`, `serve`, `lint`, or `test`. Run with `nx `. +- _Target_: A specific task that can be performed on a project, like `build`, `serve`, `lint`, or + `test`. Run with `nx `. -- *Executor*: The code responsible for performing a target's action, typically provided by Nx plugins (e.g., `@nx/webpack:webpack` for running Webpack builds). +- _Executor_: The code responsible for performing a target's action, typically provided by Nx + plugins (e.g., `@nx/webpack:webpack` for running Webpack builds). -- *Configuration*: A named set of options for running a target in different modes, accessed via the `--configuration` flag (e.g., `nx build browser --configuration=chrome-mv3`). +- _Configuration_: A named set of options for running a target in different modes, accessed via the + `--configuration` flag (e.g., `nx build browser --configuration=chrome-mv3`). ## Using Nx To use the Nx cli you have two options: -1. Install nx globally +1. Install nx globally 2. Use npx ## Common Commands @@ -114,17 +134,21 @@ Nx stores its cache in the `.nx/cache` directory at the root of the repository. - Build artifacts - Metadata about the task execution -The cache is created based on the inputs defined for each target. If none of the inputs have changed, Nx will restore the previous output. +The cache is created based on the inputs defined for each target. If none of the inputs have +changed, Nx will restore the previous output. ## Contributing Guidelines When contributing to Bitwarden with Nx: -1. *Use Nx commands* from the repository root instead of navigating to individual project directories. -2. *Respect project boundaries* - imports between projects should follow the established dependency graph. -3. *When adding new dependencies* between projects, ensure they're reflected in imports in the code. -4. *For new scripts or build steps*, add them to the appropriate project's `project.json` file rather than to individual `package.json` files. -5. *Test affected projects* before submitting a PR: +1. _Use Nx commands_ from the repository root instead of navigating to individual project + directories. +2. _Respect project boundaries_ - imports between projects should follow the established dependency + graph. +3. _When adding new dependencies_ between projects, ensure they're reflected in imports in the code. +4. _For new scripts or build steps_, add them to the appropriate project's `project.json` file + rather than to individual `package.json` files. +5. _Test affected projects_ before submitting a PR: ```bash nx affected --target=test nx affected --target=lint From 58fdd55b0ed1707c514a8932555b08290f961023 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 28 May 2025 13:34:54 -0400 Subject: [PATCH 07/10] remove the answers to nx g for now until I get them set up better --- docs/contributing/creating-a-lib.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/contributing/creating-a-lib.md b/docs/contributing/creating-a-lib.md index b5317f15..06611a0b 100644 --- a/docs/contributing/creating-a-lib.md +++ b/docs/contributing/creating-a-lib.md @@ -24,9 +24,8 @@ We use the `@nx/js` plugin to generate new libraries. Follow these steps to crea settings: - **Which bundler would you like to use?** Select `none` (we use our own bundling setup) - - **What should be the project name?** The same name as your library (e.g., `my-new-lib`) - - **Would you like to add a package.json?** Select `Yes` - - **Which unit test runner would you like to use?** Select `jest` (our standard test runner) + - **Which linter would you like to use?** + - **Which unit test runner would you like to use?** 3. Once generated, the library will have this structure: From 2916d321269c20e6ae013e01c1f32ddeb73844b9 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Thu, 29 May 2025 07:26:54 -0400 Subject: [PATCH 08/10] Add design a lib section from confluence doc --- docs/contributing/creating-a-lib.md | 146 ++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/docs/contributing/creating-a-lib.md b/docs/contributing/creating-a-lib.md index 06611a0b..d15876da 100644 --- a/docs/contributing/creating-a-lib.md +++ b/docs/contributing/creating-a-lib.md @@ -84,3 +84,149 @@ import { someFunction } from "@bitwarden/my-new-lib"; - Write unit tests for your library's functionality - If your library has dependencies on other libraries, make sure to add them to the `project.json` file + +## Designing a Library + +There are a few ways you and your team may want to design a library, there are a lot of factors to +take into account like clean organization, ease of onboarding new members, simplicity of moving +ownership to a new team, and optimizing for module size. Below are a few ways you might want to +design a library. + +### Option 1: Feature-based libraries + +One strategy to employ is a feature-scoped library. + +The import for such a library would be `@bitwarden/[feature]`. For example the `global-state` +feature would be imported with `@bitwarden/global-state`. + +If the feature has both a UI component and needs to be used in the CLI it would probably result in a +`@bitwarden/[feature]-ui` or `@bitwarden/[feature]-angular` library as well. + +:::note + +With more things being added to the SDK and the CLI eventually being written directly in Rust there +will become less and less need to have a package with an Angular dependency and without it. + +::: + +Pros + +- You'll have smaller libraries that have minimal dependencies, making them easier for another team + to depend on without a circular reference. +- If your team is ever split or a feature you own is moved to another team this can likely be done + with just an update to the GitHub `CODEOWNERS` file. +- You'll have a clearer dependency graph. +- Your modules will be smaller. + +Cons + +- YOu have to spend the time to think about and define what a feature is. +- You have to create libraries somewhat often. +- You MAY need "glue" libraries still. +- It is not as clear who owns what feature from looking at library names. + +:::info Glue libraries + +A "Glue" library is a library that might not exist other than the need for two teams to collaborate +on a cross cutting feature. The glue library might exist to hold an interface that team B is +expected to implement but team A will be responsible for consuming. This helps glue 2 features +together while still allowing for team A to consume other things that exist in team B's library but +still avoid a circular dependency. + +::: + +### Option 2: Team-based libraries + +Another strategy would be to have a library for the vast majority of your teams code in a single +package. + +There are many ways you may choose to design the library, but if it's one library you will need to +be dependent on everything that makes all your features tick. + +**If all teams go this route it will be impossible to only have these team libraries**. Why? Because +the team grouping is very likely to result in circular dependencies. For example, if team A depends +on team B's library then team B cannot depend on anything in team A's library. If team B did need +something they would need to request that team A move it "downstream". Team A would need to move +their code and come up with a name for it. If they don't want to also re-export those pieces of code +they will need to update the import for every other place that code of used. You may also have to +deal with the code now being separated from similar code or you may decide to move that code too. + +Pros + +- You have fewer libraries to maintain. +- All your team’s code is in one place. + +Cons + +- You'll need to move code ad-hoc more often to make glue libraries, and each time try to think + about how to design the package abstraction. +- If your team splits you will need to move a lot more code. +- You’ll have larger modules. + +### Option 3: Type-based libraries + +You can also split libraries based on the primary kind of file that it holds. For example, you could +have a library holding all `type`’s, one for `abstractions`, and on more `services`. Since one +library for all type’s would be mean having a library that has multiple owners it would be highly +discouraged and therefore this would likely be split by team as well, resulting in packages like +`@bitwarden/platform-types`; this library strategy is really a subset of the team-based one. + +Pros + +- You’ll be less likely to have circular dependencies within your team’s code, since generally Types + < Abstractions < Services where < means lower level. +- It’s most similar to the organization strategy we’ve had for a while. + +Cons + +- There is no guarantee that all the types for a given team are lower level than all the types of + another team that they need to depend on. Circular dependencies can still happen amongst teams. +- It’s also possible for a type to need to depend on an abstraction, +- We are generally discouraging teams from making abstractions unless needed (link). + +### Option 4: Feature/type hybrid + +Another strategy could be to split libraries by the kind of item in feature-scoped libraries. + +Pros + +- Lowest chance of circular dependencies showing up later. +- Pretty easy to move ownership. + +Cons + +- The most libraries to maintain. +- Consumers will likely have to import multiple modules in order to use your feature. + +### Platform Recommendation + +Given the options available with Nx and the pros and cons of each, Platform is planning on using +[feature-based libraries](#option-1-feature-based-libraries) and we recommend other teams do as +well. + +We understand that we might have a domain that is a little easier to split into features, but we +believe that the work is worthwhile for teams to do. + +There will be some instances that our libraries may only contain abstractions and very simple types +which would then resemble the type-based approach. We will be forced to do this because we have some +things like storage where it’s only really useful which the implementations made in the individual +apps. + +Example Platform feature libraries (all of these would be imported like `@bitwarden/[feature]`: + +- `storage-core` +- `user-state` +- `global-state` +- `state` (will have its own code but also a meta package re-exporting `user-state` and + `global-state`) +- `clipboard` +- `messaging` +- `ipc` +- `config` +- `http` +- `i18n` +- `environments` +- `server-notifications` +- `sync` + +Hopefully these will give you some ideas for how to split up your own features. From 6459523a80fc879788cbb15a2f3a9d1d2af7ce41 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Tue, 3 Jun 2025 15:12:59 -0400 Subject: [PATCH 09/10] reorg --- .../contributing/development-tooling/index.md | 1 + .../nx/generators/basic-lib-generator.md | 248 ++++++++++++++++++ .../nx/generators/bitwarden-nx-plugin.md | 54 ++++ .../nx/generators/index.md | 64 +++++ .../nx/index.md} | 8 +- 5 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 docs/contributing/development-tooling/index.md create mode 100644 docs/contributing/development-tooling/nx/generators/basic-lib-generator.md create mode 100644 docs/contributing/development-tooling/nx/generators/bitwarden-nx-plugin.md create mode 100644 docs/contributing/development-tooling/nx/generators/index.md rename docs/contributing/{nx.md => development-tooling/nx/index.md} (97%) diff --git a/docs/contributing/development-tooling/index.md b/docs/contributing/development-tooling/index.md new file mode 100644 index 00000000..8af3f3ae --- /dev/null +++ b/docs/contributing/development-tooling/index.md @@ -0,0 +1 @@ +# Development Tooling diff --git a/docs/contributing/development-tooling/nx/generators/basic-lib-generator.md b/docs/contributing/development-tooling/nx/generators/basic-lib-generator.md new file mode 100644 index 00000000..1671d7b1 --- /dev/null +++ b/docs/contributing/development-tooling/nx/generators/basic-lib-generator.md @@ -0,0 +1,248 @@ +--- +title: Basic Library Generator +--- + +# Using the basic-lib Generator + +The `basic-lib` generator creates a new library with Bitwarden's standard configuration and +structure. It sets up all the necessary files, configurations, and hooks with global configuration +files. + +## Command Syntax + +You can use the basic lib generator by running this command: + +```bash +npx nx g @bitwarden/nx-plugin:basic-lib +``` + +## Available Options + +All fields are required, but do not need to be supplied as CLI flags. Generator users will be asked +interactively for each of these if they are not supplied as CLI flags. + +| Option | Description | Required | Default | +| --------------- | ----------------------------------------------- | -------- | ------- | +| `--name` | The name of the library | Yes | - | +| `--description` | A brief description of the library | Yes | none | +| `--team` | The team responsible for the library | Yes | none | +| `--directory` | The directory where the library will be created | Yes | "libs" | + +## Step-by-Step Example + +Let's create a new utility library called "password-insulter": + +1. Open your terminal and navigate to the root of the Bitwarden clients repository +2. Run the generator command: + ```bash + nx g @bitwarden/nx-plugin:basic-lib --name=password-insulter --description="Like the password strength meter, but more judgemental" --team=tools + ``` +3. The generator will create the library structure and update necessary configuration files +4. The new library is now ready to use + +## What Gets Generated + +The generator creates the following: + +- **Library Structure**: + + - `libs/password-insulter/` + - `src/` + - `index.ts` - Main entry point + - `README.md` - With the provided description + - `package.json` - Very minimal + - `tsconfig.json` - TypeScript configuration + - `tsconfig.lib.json` - Library-specific TypeScript configuration + - `tsconfig.spec.json` - Test-specific TypeScript configuration + - `jest.config.js` - Test configuration + - `.eslintrc.json` - Linting rules + +- **Configuration Updates**: + +The generator then updates `tsconfig.base.json` to reference your new library, updates CODEOWNERS to +assign the team to the new folder, and runs `npm i` to link everything up. + +## Post-Generation Next Steps + +After generating your library: + +1. Review the generated README.md and update it with more detailed information if needed +2. Implement your library code in the `src/` directory, using whatever subfolder structure you + prefer +3. Export public APIs through the `src/index.ts` file +4. Write tests for your library +5. Build your library with `npx nx build password-insulter` +6. Lint your library with `npx nx lint password-insulter` +7. Test your library with `npx nx test password-insulter` + +## Troubleshooting Common Issues + +### Issue: Generator fails with path errors + +**Solution**: Ensure you're running the command from the root of the repository. + +### Issue: TypeScript path mapping not working + +**Solution**: Run `nx reset` to clear the Nx cache, then try importing from your library again. + +## Extending the Generated Code + +The generated library provides a basic structure that you can extend: + +- Add additional directories for specific features +- Create subdirectories in `src/` for better organization +- Modify the Jest configuration for specialized testing needs + +## Designing a Library + +There are a few ways you and your team may want to design a library, there are a lot of factors to +take into account like clean organization, ease of onboarding new members, simplicity of moving +ownership to a new team, and optimizing for module size. Below are a few ways you might want to +design a library. + +### Option 1: Feature-based libraries + +One strategy to employ is a feature-scoped library. + +The import for such a library would be `@bitwarden/[feature]`. For example the `global-state` +feature would be imported with `@bitwarden/global-state`. + +If the feature has both a UI component and needs to be used in the CLI it would probably result in a +`@bitwarden/[feature]-ui` or `@bitwarden/[feature]-angular` library as well. + +:::note + +With more things being added to the SDK and the CLI eventually being written directly in Rust there +will become less and less need to have a package with an Angular dependency and without it. + +::: + +Pros + +- You'll have smaller libraries that have minimal dependencies, making them easier for another team + to depend on without a circular reference. +- If your team is ever split or a feature you own is moved to another team this can likely be done + with just an update to the GitHub `CODEOWNERS` file. +- You'll have a clearer dependency graph. +- Your modules will be smaller. + +Cons + +- YOu have to spend the time to think about and define what a feature is. +- You have to create libraries somewhat often. +- You MAY need "glue" libraries still. +- It is not as clear who owns what feature from looking at library names. + +:::info Glue libraries + +A "Glue" library is a library that might not exist other than the need for two teams to collaborate +on a cross cutting feature. The glue library might exist to hold an interface that team B is +expected to implement but team A will be responsible for consuming. This helps glue 2 features +together while still allowing for team A to consume other things that exist in team B's library but +still avoid a circular dependency. + +::: + +### Option 2: Team-based libraries + +Another strategy would be to have a library for the vast majority of your teams code in a single +package. + +There are many ways you may choose to design the library, but if it's one library you will need to +be dependent on everything that makes all your features tick. + +**If all teams go this route it will be impossible to only have these team libraries**. Why? Because +the team grouping is very likely to result in circular dependencies. For example, if team A depends +on team B's library then team B cannot depend on anything in team A's library. If team B did need +something they would need to request that team A move it "downstream". Team A would need to move +their code and come up with a name for it. If they don't want to also re-export those pieces of code +they will need to update the import for every other place that code of used. You may also have to +deal with the code now being separated from similar code or you may decide to move that code too. + +Pros + +- You have fewer libraries to maintain. +- All your team’s code is in one place. + +Cons + +- You'll need to move code ad-hoc more often to make glue libraries, and each time try to think + about how to design the package abstraction. +- If your team splits you will need to move a lot more code. +- You’ll have larger modules. + +### Option 3: Type-based libraries + +You can also split libraries based on the primary kind of file that it holds. For example, you could +have a library holding all `type`’s, one for `abstractions`, and on more `services`. Since one +library for all type’s would be mean having a library that has multiple owners it would be highly +discouraged and therefore this would likely be split by team as well, resulting in packages like +`@bitwarden/platform-types`; this library strategy is really a subset of the team-based one. + +Pros + +- You’ll be less likely to have circular dependencies within your team’s code, since generally Types + < Abstractions < Services where < means lower level. +- It’s most similar to the organization strategy we’ve had for a while. + +Cons + +- There is no guarantee that all the types for a given team are lower level than all the types of + another team that they need to depend on. Circular dependencies can still happen amongst teams. +- It’s also possible for a type to need to depend on an abstraction, +- We are generally discouraging teams from making abstractions unless needed (link). + +### Option 4: Feature/type hybrid + +Another strategy could be to split libraries by the kind of item in feature-scoped libraries. + +Pros + +- Lowest chance of circular dependencies showing up later. +- Pretty easy to move ownership. + +Cons + +- The most libraries to maintain. +- Consumers will likely have to import multiple modules in order to use your feature. + +### Platform Recommendation + +Given the options available with Nx and the pros and cons of each, Platform is planning on using +[feature-based libraries](#option-1-feature-based-libraries) and we recommend other teams do as +well. + +We understand that we might have a domain that is a little easier to split into features, but we +believe that the work is worthwhile for teams to do. + +There will be some instances that our libraries may only contain abstractions and very simple types +which would then resemble the type-based approach. We will be forced to do this because we have some +things like storage where it’s only really useful which the implementations made in the individual +apps. + +Example Platform feature libraries (all of these would be imported like `@bitwarden/[feature]`: + +- `storage-core` +- `user-state` +- `global-state` +- `state` (will have its own code but also a meta package re-exporting `user-state` and + `global-state`) +- `clipboard` +- `messaging` +- `ipc` +- `config` +- `http` +- `i18n` +- `environments` +- `server-notifications` +- `sync` + +Hopefully these will give you some ideas for how to split up your own features. + +## Further Learning + +For more information about Nx libraries and generators: + +- [Nx Library Generation](https://nx.dev/plugin-features/create-libraries) +- [Nx Library Types](https://nx.dev/more-concepts/library-types) +- [Nx Project Configuration](https://nx.dev/reference/project-configuration) diff --git a/docs/contributing/development-tooling/nx/generators/bitwarden-nx-plugin.md b/docs/contributing/development-tooling/nx/generators/bitwarden-nx-plugin.md new file mode 100644 index 00000000..c4d42957 --- /dev/null +++ b/docs/contributing/development-tooling/nx/generators/bitwarden-nx-plugin.md @@ -0,0 +1,54 @@ +--- +title: Bitwarden Nx Plugin +--- + +# @bitwarden/nx-plugin + +The `@bitwarden/nx-plugin` is a custom Nx plugin developed specifically for Bitwarden projects. It +provides generators tailored to Bitwarden's architecture and coding standards. + +## Overview + +This plugin extends Nx's capabilities with Bitwarden-specific generators that help maintain +consistency across the codebase. It automates the creation of libraries, components, and other +project elements according to Bitwarden's established patterns. + +## How It Fits Into the Project Architecture + +The `@bitwarden/nx-plugin` is designed to: + +1. Enforce Bitwarden's architectural decisions and code organization +2. Streamline the creation of new libraries and components +3. Ensure consistent configuration across the project +4. Automate updates to project metadata and configuration files +5. Reduce the learning curve for new contributors + +By using this plugin, we maintain a consistent approach to code organization and structure across +the entire project. + +## Installation and Setup + +The plugin is included as a development dependency in the project. If you're working with a fresh +clone of the repository, it will be installed when you run: + +```bash +npm install +``` + +No additional setup is required to use the generators provided by the plugin. + +## Available Generators + +The plugin currently includes the following generators: + +- `basic-lib`: Creates a new library with standard configuration and structure + +Additional generators may be added in the future to support other common patterns in the Bitwarden +codebase. + +## Further Learning + +To learn more about Nx plugins and how they work: + +- [Nx Plugin Development](https://nx.dev/extending-nx/creating-nx-plugins) +- [Nx Plugins Overview](https://nx.dev/extending-nx/intro) diff --git a/docs/contributing/development-tooling/nx/generators/index.md b/docs/contributing/development-tooling/nx/generators/index.md new file mode 100644 index 00000000..79df4847 --- /dev/null +++ b/docs/contributing/development-tooling/nx/generators/index.md @@ -0,0 +1,64 @@ +--- +title: Nx Generators +--- + +# Nx Generators + +Nx generators are powerful tools that automate the creation of code, configuration, and other files +in your project. They help maintain consistency across your codebase and reduce the manual effort +required to scaffold new components, libraries, or applications. + +## What are Nx Generators? + +Nx generators (previously known as schematics) are code generation tools that follow templates to +create or modify files in your project. They can: + +- Create new files from templates +- Modify existing files +- Update configuration files +- Ensure consistent project structure +- Automate repetitive tasks + +Generators can be run using the Nx CLI with the `nx generate` command (or the shorthand `nx g`). + +## When to Use Generators + +Use generators when: + +- Creating new libraries, components, or features that follow a standard pattern +- You want to ensure consistency across similar parts of your application +- You need to automate repetitive setup tasks +- You want to reduce the chance of human error in project setup + +## Why Use Generators Instead of Manual Creation + +- **Consistency**: Generators ensure that all generated code follows the same patterns and + conventions +- **Efficiency**: Save time by automating repetitive tasks +- **Reduced Errors**: Minimize human error in project setup +- **Maintainability**: Easier to maintain code that follows consistent patterns +- **Onboarding**: Help new team members create code that follows project standards + +## How Bitwarden Uses Generators + +Platform maintains a Nx plugin (`@bitwarden/nx-plugin`) with custom generators for our monorepo. We +may at times also use stock generators from dependencies such as Nx's own generator for generating +nx plugins (a generator generator generator, if you will). + +## Creating A Nx Generator + +It may be useful at some point to create a new Nx generator. The platform team maintains a nx plugin +in `libs/nx-plugin` that has a generators folder. If you need to create a new generator please do so +by following these steps. + +1. Run + `npx nx generate @nx/plugin:generator libs/nx-plugin/src/generators/your-generator-name-here}`. + This will create a basic generator structure for you to get started with. + +## Further Learning + +For more information about Nx generators, check out these resources: + +- [Nx Generators Documentation](https://nx.dev/plugin-features/use-code-generators) +- [Creating Custom Generators](https://nx.dev/recipes/generators/creating-files) +- [Nx Generator Examples](https://nx.dev/plugin-features/use-code-generators#examples) diff --git a/docs/contributing/nx.md b/docs/contributing/development-tooling/nx/index.md similarity index 97% rename from docs/contributing/nx.md rename to docs/contributing/development-tooling/nx/index.md index 10771858..0bb90968 100644 --- a/docs/contributing/nx.md +++ b/docs/contributing/development-tooling/nx/index.md @@ -1,6 +1,4 @@ -# Nx at Bitwarden - -## What is Nx? +# Nx Nx is a powerful open-source build system designed specifically for monorepo development. It provides tools and techniques for enhancing developer productivity, optimizing CI performance, and @@ -53,6 +51,10 @@ Each app and library can be defined as a project with a `project.json` file that configurations, targets, and other settings. This replaces many scripts previously defined in individual `package.json` files. +### 5. Code Generation + +Nx ships with powerful tools for automating the creation of things like libraries. + ## Key Nx Terminology - _nx.json_: The primary configuration file for the Nx workspace, located at the root. Defines From c356abb293974b574530afe14e1980d507974669 Mon Sep 17 00:00:00 2001 From: Addison Beck Date: Wed, 2 Jul 2025 18:51:39 -0400 Subject: [PATCH 10/10] Delete docs/contributing/creating-a-lib.md --- docs/contributing/creating-a-lib.md | 232 ---------------------------- 1 file changed, 232 deletions(-) delete mode 100644 docs/contributing/creating-a-lib.md diff --git a/docs/contributing/creating-a-lib.md b/docs/contributing/creating-a-lib.md deleted file mode 100644 index d15876da..00000000 --- a/docs/contributing/creating-a-lib.md +++ /dev/null @@ -1,232 +0,0 @@ -# Creating a New Library - -This guide explains how to create a new library in the Bitwarden monorepo using Nx. - -## Prerequisites - -- Node.js and npm installed -- Bitwarden monorepo cloned and dependencies installed -- The Nx cli installed, or accessible over npx - -## Creating a Library with Nx - -We use the `@nx/js` plugin to generate new libraries. Follow these steps to create a new library: - -1. From the root of the monorepo, run the generator command: - - ```bash - nx g @nx/js:lib my-new-lib --directory=libs/my-new-lib - ``` - - Replace `my-new-lib` with the name of your library. - -2. The generator will ask you questions about the library configuration. Here are recommended - settings: - - - **Which bundler would you like to use?** Select `none` (we use our own bundling setup) - - **Which linter would you like to use?** - - **Which unit test runner would you like to use?** - -3. Once generated, the library will have this structure: - -``` -libs/ - my-new-lib/ - src/ - index.ts - my-new-lib.ts - tsconfig.json - project.json - package.json - jest.config.js -``` - -4. Update the `package.json` with the appropriate Bitwarden naming convention: - -```json -{ - "name": "@bitwarden/my-new-lib", - "version": "0.0.0" -} -``` - -5. Update the library's path in the root `tsconfig.base.json` file to make it available to other - projects: - -```json -{ - "compilerOptions": { - "paths": { - "@bitwarden/my-new-lib": ["libs/my-new-lib/src"] - } - } -} -``` - -## Building and Testing the Library - -- Build the library: `nx build my-new-lib` -- Test the library: `nx test my-new-lib` -- Lint the library: `nx lint my-new-lib` - -## Importing Your Library in Other Projects - -Once your library is created and built, you can import it in other projects within the monorepo: - -```ts -import { someFunction } from "@bitwarden/my-new-lib"; -``` - -## Best Practices - -- Follow the Bitwarden code style guidelines -- Document your library's public API -- Write unit tests for your library's functionality -- If your library has dependencies on other libraries, make sure to add them to the `project.json` - file - -## Designing a Library - -There are a few ways you and your team may want to design a library, there are a lot of factors to -take into account like clean organization, ease of onboarding new members, simplicity of moving -ownership to a new team, and optimizing for module size. Below are a few ways you might want to -design a library. - -### Option 1: Feature-based libraries - -One strategy to employ is a feature-scoped library. - -The import for such a library would be `@bitwarden/[feature]`. For example the `global-state` -feature would be imported with `@bitwarden/global-state`. - -If the feature has both a UI component and needs to be used in the CLI it would probably result in a -`@bitwarden/[feature]-ui` or `@bitwarden/[feature]-angular` library as well. - -:::note - -With more things being added to the SDK and the CLI eventually being written directly in Rust there -will become less and less need to have a package with an Angular dependency and without it. - -::: - -Pros - -- You'll have smaller libraries that have minimal dependencies, making them easier for another team - to depend on without a circular reference. -- If your team is ever split or a feature you own is moved to another team this can likely be done - with just an update to the GitHub `CODEOWNERS` file. -- You'll have a clearer dependency graph. -- Your modules will be smaller. - -Cons - -- YOu have to spend the time to think about and define what a feature is. -- You have to create libraries somewhat often. -- You MAY need "glue" libraries still. -- It is not as clear who owns what feature from looking at library names. - -:::info Glue libraries - -A "Glue" library is a library that might not exist other than the need for two teams to collaborate -on a cross cutting feature. The glue library might exist to hold an interface that team B is -expected to implement but team A will be responsible for consuming. This helps glue 2 features -together while still allowing for team A to consume other things that exist in team B's library but -still avoid a circular dependency. - -::: - -### Option 2: Team-based libraries - -Another strategy would be to have a library for the vast majority of your teams code in a single -package. - -There are many ways you may choose to design the library, but if it's one library you will need to -be dependent on everything that makes all your features tick. - -**If all teams go this route it will be impossible to only have these team libraries**. Why? Because -the team grouping is very likely to result in circular dependencies. For example, if team A depends -on team B's library then team B cannot depend on anything in team A's library. If team B did need -something they would need to request that team A move it "downstream". Team A would need to move -their code and come up with a name for it. If they don't want to also re-export those pieces of code -they will need to update the import for every other place that code of used. You may also have to -deal with the code now being separated from similar code or you may decide to move that code too. - -Pros - -- You have fewer libraries to maintain. -- All your team’s code is in one place. - -Cons - -- You'll need to move code ad-hoc more often to make glue libraries, and each time try to think - about how to design the package abstraction. -- If your team splits you will need to move a lot more code. -- You’ll have larger modules. - -### Option 3: Type-based libraries - -You can also split libraries based on the primary kind of file that it holds. For example, you could -have a library holding all `type`’s, one for `abstractions`, and on more `services`. Since one -library for all type’s would be mean having a library that has multiple owners it would be highly -discouraged and therefore this would likely be split by team as well, resulting in packages like -`@bitwarden/platform-types`; this library strategy is really a subset of the team-based one. - -Pros - -- You’ll be less likely to have circular dependencies within your team’s code, since generally Types - < Abstractions < Services where < means lower level. -- It’s most similar to the organization strategy we’ve had for a while. - -Cons - -- There is no guarantee that all the types for a given team are lower level than all the types of - another team that they need to depend on. Circular dependencies can still happen amongst teams. -- It’s also possible for a type to need to depend on an abstraction, -- We are generally discouraging teams from making abstractions unless needed (link). - -### Option 4: Feature/type hybrid - -Another strategy could be to split libraries by the kind of item in feature-scoped libraries. - -Pros - -- Lowest chance of circular dependencies showing up later. -- Pretty easy to move ownership. - -Cons - -- The most libraries to maintain. -- Consumers will likely have to import multiple modules in order to use your feature. - -### Platform Recommendation - -Given the options available with Nx and the pros and cons of each, Platform is planning on using -[feature-based libraries](#option-1-feature-based-libraries) and we recommend other teams do as -well. - -We understand that we might have a domain that is a little easier to split into features, but we -believe that the work is worthwhile for teams to do. - -There will be some instances that our libraries may only contain abstractions and very simple types -which would then resemble the type-based approach. We will be forced to do this because we have some -things like storage where it’s only really useful which the implementations made in the individual -apps. - -Example Platform feature libraries (all of these would be imported like `@bitwarden/[feature]`: - -- `storage-core` -- `user-state` -- `global-state` -- `state` (will have its own code but also a meta package re-exporting `user-state` and - `global-state`) -- `clipboard` -- `messaging` -- `ipc` -- `config` -- `http` -- `i18n` -- `environments` -- `server-notifications` -- `sync` - -Hopefully these will give you some ideas for how to split up your own features.