diff --git a/libs/nx-plugin/README.md b/libs/nx-plugin/README.md index 580a7eb72c..df32b10613 100644 --- a/libs/nx-plugin/README.md +++ b/libs/nx-plugin/README.md @@ -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) diff --git a/libs/nx-plugin/docs/using-the-basic-lib-generator.md b/libs/nx-plugin/docs/using-the-basic-lib-generator.md new file mode 100644 index 0000000000..34834f9544 --- /dev/null +++ b/libs/nx-plugin/docs/using-the-basic-lib-generator.md @@ -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)