DEV Community

Josh Alphonse for ByteDance Open Source

Posted on • Updated on

Monorepos with Rspack Just Makes Things Easier

Introduction

When it comes to web dev, it's hard to keep up with all the latest trends. I also can't come up with many examples of ecosystems that move faster than JavaScript's. It feels as if a new version of the iPhone is released every month. However, just because things are moving fast, doesn't mean you have to be left in the dust.

Monorepos are one of the hottest additions to the scene especially for web developers. One of the biggest challenges when building a full stack app is managing complexity. The larger your application becomes, the more complex it gets and it doesn't matter how big your organization or team may be. You've probably experienced this kind of struggle if you're building a product that has many different front-end code bases that share backend's and interfaces.

The way orgs are solving this issue now is with the use of monorepos. Monorepos help mitigate the confusion that comes along with having to manage multiple repositories for multiple distinct projects/products. A monorepo can consolidate your projects all into one repository. Contrary to the name, monorepos are technically not monolithic. A common misconception is that if you use a monorepo, every project in the repository have to be released on the same day. This acutally isn't the case. Just because we are developing our code in the same place doesn't mean we have to deploy everything at the same time nor to the same place. Since your code bases can exist in one repository, it makes common tasks like code sharing and refactoring way easier. This leads to significantly lowering the cost that comes with creating libs, microservices and microfronents. If you want true development flexibility, then you have to give this approach a try. This is why some of the biggest companies are implementing monorepos.

Rspack

So, where does Rspack come into play here?

As you may know, Rspack is a high performance web bundler that offers interoperability with the webpack ecosystem but also build systems like Nx to build smart monorepos! Rspack can bring several key benefits when used in conjunction with NX, primarily by enhancing the performance aspects of the development and build processes. Here are a few ways Rspack can positively impact an NX-powered project :

  1. Speeding up builds:Rspack is known for its quick bundling capabilities. In complex NX workspaces with multiple applications and libraries, faster bundling translates to quicker build times. This is especially beneficial when you're dealing with large-scale projects where build times can significantly impact developer productivity and continuous integration/deployment pipelines.
  2. Reducing setup time:Rspack has a reputation for requiring less configuration out of the box compared to other bundlers, which can simplify the setup process. In an NX workspace, this means you can get applications up and running more quickly, and you can spend less time adjusting bundler configurations and more time developing features for the e-commerce store.
  3. Efficient code splitting: Rspack can facilitate code splitting, which means that each page or feature can have its bundle. This ensures that users are only downloading the code necessary for the page they're visiting, improving load times and enhancing the user experience.
  4. Providing Hot Module Replacement (HMR)While NX manages the overall structure and dependencies in your monorepo, Rspack can provide Hot Module Replacement for a smoother development experience. HMR allows changes to be applied to a running application without needing a full reload, preserving the application state and speeding up the development process.

Although monorepos can solve a bunch of problems for applications of any size, there is one feature in particular that I'm going to highlight in this blog post. That feature being the ability to manage aliases with Rspack and Nx.

Defining aliases with rspack in an NX monorepo provides multiple benefits and solves several issues commonly faced in large-scale projects. Let's take a look before we dive into the tutorial:

  1. Simplified Imports: With aliases, you can avoid long and complicated relative paths that are hard to read and maintain. Aliases provide a clear, concise way to import modules.
// Without alias... How do you even know where your functinos are?! Half the time in guesssing.
import utility from '../../../../libs/utils/src/utility';

// With alias
import utility from '@utils/utility';
Enter fullscreen mode Exit fullscreen mode
  1. Easier Refactoring: When you decide to rearrange your project's folder structure, you won't need to update every import statement. Instead, you only update the alias paths in the rspack configuration.
  2. Code Readability: Aliases can help signify the intent or the origin of the imported module more clearly than relative paths.
  3. Consistency Across Apps and Libs: In a monorepo, you typically have multiple apps and libraries. Aliases ensure that every part of your project refers to shared libs in the same way.
  4. Namespace Clarity: By using aliases that represent features or shared libraries (like @feature-a or @shared), you provide a clear namespace. This indicates that the import is from a shared source and not a local module.
  5. Avoid File Path Errors: As projects grow, relative paths become more prone to errors when files are moved or when the developer is unsure of the current file's depth in the directory structure.
  6. Enhanced Autocompletion: Many IDEs can provide improved autocompletion for imports when aliases are set up properly, making development faster and reducing typos.
  7. Collaboration Enhancement: When working with a team, aliases ensure that everyone is using the same paths to common resources, reducing the cognitive load of understanding where a file is located within the project hierarchy.
  8. Integration with Build Tools: Tools like rspack can understand these aliases and use them to resolve the actual bundle paths during the build process, ensuring the generated bundles reflect the same structure and readability of the source code.
  9. Decoupling the Code: Aliases can help in abstracting the actual file system paths, which means developers can think more about architecture and less about the file system.

Tutorial

Alright, so now that we have some base knowledge of what monorepos are, let's jump into a tutorial. This time around, we're going to set up a monorepo for an e-commerce store using NX and Rspack. We'll configure aliases to simplify module resolution, keeping our import statements clean and readable.

Before we get started

Prerequisites

  • At the minimum, a basic understanding of Javascript/Typescript and react
  • Node.js, yarn, or pnpm
  • Install Nx globally depending on your package manager. I'm using npm add --global nx@latest
  • Create a project with npx create-nx-workspace myrspackapp --preset=@nx/rspack

Step 1: Creating an NX Workspace

First, we need to create a new NX workspace by running the following command:

npx create-nx-workspace monrepo-example --preset=@nx/rspack
Enter fullscreen mode Exit fullscreen mode

Choose empty as the preset for full customization. Once the setup is complete, navigate into your new workspace directory:

cd monorepo-example
Enter fullscreen mode Exit fullscreen mode

Step 2: Adding Applications and Libraries

With NX, you can have multiple apps and libraries co-existing in a single monorepo. Since we installed Nx globally, we can use the nx command to generate our libraries.

For this example, let's add an application and two libraries:

nx generate @nrwl/react:application store-front
nx generate @nrwl/react:library ui-shared --directory=shared
nx generate @nrwl/react:library product-data --directory=shared
Enter fullscreen mode Exit fullscreen mode

This will set up a React application (store-front) and two shared libraries (ui-shared and product-data) under the libs/shared/ directory.

Step 3: Configuring Aliases in rspack.config.js

Create a rspack.config.js file at the root of your workspace if it doesn't exist, and define your aliases:

const path = require('path');

module.exports = (config) => {
  config.resolve.alias = {
    ...config.resolve.alias,

    // For that beautiful UI:
    '@ui-elements': path.resolve(__dirname, 'libs/shared/ui-elements/src'),

    // For the business logic behind products:
    '@product-services': path.resolve(__dirname, 'libs/shared/product-services/src'),

    // Continue as your project grows...
  };
  return config;
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Using Aliases in Your Code

Now that we have our aliases, we can use them within our applications and libraries. For example, in our store-front app, we can now import from our shared libraries like so:

import { CheckoutButton, StoreBanner } from '@ui-elements';
import { fetchAllProducts } from '@product-services';
Enter fullscreen mode Exit fullscreen mode

Step 5: Building the Application

To build your application with the new alias configurations, simply use the NX build command:

nx build store-front
Enter fullscreen mode Exit fullscreen mode

NX will invoke Rspack with your custom configuration including the aliases, and you should see a successful build output.

Conclusion

By using aliases with rspack in an NX monorepo, you're essentially laying down a scalable, maintainable foundation that streamlines development workflows and helps manage complexity as your project grows. Keep an eye out for more content and if you have any questions and want to join the community find us on Github and Join us on our ByteDance Open Source Discord Server!

Top comments (0)