mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
docs: document the basic-lib generator (#15891)
This commit is contained in:
@@ -1,5 +1,83 @@
|
||||
# nx-plugin
|
||||
# @bitwarden/nx-plugin
|
||||
|
||||
Owned by: Platform
|
||||
The `@bitwarden/nx-plugin` library is a custom Nx plugin developed specifically for Bitwarden projects. It
|
||||
provides generators tailored to Bitwarden's architecture and coding standards.
|
||||
|
||||
Custom Nx tools like generators and executors for Bitwarden projects
|
||||
## Overview
|
||||
|
||||
This plugin extends Nx's capabilities with Bitwarden-specific **nx generators** that help maintain
|
||||
consistency across the codebase.
|
||||
|
||||
### What are Nx Generators?
|
||||
|
||||
Nx generators 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
|
||||
|
||||
If you're familiar with the code generation tools of say, the angular CLI, then you can just think of =nx= generators as that but on a larger scale. 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
|
||||
|
||||
## How `@bitwarden/nx-plugin` Fits Into the Project Architecture
|
||||
|
||||
`@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. Specific documentation for the `basic-lib` generator can be found [here](./docs/using-the-basic-lib-generator.md)
|
||||
|
||||
Additional generators may be added in the future to support other common patterns in the Bitwarden
|
||||
codebase.
|
||||
|
||||
## Creating A Nx Generator
|
||||
|
||||
This library is maintained by platform, but anyone from any team can add a
|
||||
generator if there is any amount of value added. If you need to create a new
|
||||
generator please do so by running
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
237
libs/nx-plugin/docs/using-the-basic-lib-generator.md
Normal file
237
libs/nx-plugin/docs/using-the-basic-lib-generator.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 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.
|
||||
|
||||
> [!NOTE]
|
||||
> 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)
|
||||
Reference in New Issue
Block a user