RFC: A unified way to extend Rstack configuration #504
chenjiahan
started this conversation in
RFC
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Background
Rstack CLI already provides a single configuration entry point through
rstack.config.*and thedefine.*APIs, but it lacks a unified way to extend configuration across tools.Today, developer infrastructure teams and plugin authors extend Rstack CLI through mechanisms such as Rsbuild plugins. These mechanisms are specific to individual tools and do not provide a single way to configure linting, testing, and formatting alongside the build.
As a result, users may need to add plugins or configuration separately to
define.app,define.lib, anddefine.lintto integrate an extension across their toolchain.This RFC proposes
define.extends(), which lets projects inherit shared configuration for multiple tools at once. Shared configurations can capture team conventions, provide framework defaults, and be reused across a monorepo.Example use cases:
app-presetandlib-preset, for applications and libraries in a monorepo. Each preset covers the appropriate build, test, lint, and formatting configuration.Prior art
Several tools in the JavaScript ecosystem offer ways to inherit or compose configuration.
ESLint
ESLint uses configuration arrays and
extendsto compose shared configurations. Configurations are applied in order, with later entries able to override earlier ones. Users can also import and compose configuration objects directly. See the ESLint configuration documentation.Nuxt
Nuxt uses Layers to share configuration and application resources. A layer is a reusable foundation for a Nuxt application. It can include configuration, components, pages, layouts, and utilities, with a directory structure similar to a regular Nuxt application.
Nuxt uses
extendsto declare which layers a project inherits from. The project's own configuration has the highest priority. See Nuxt Layers.Next.js
Next.js integrations often use functions that wrap configuration objects. For example,
withMDX(nextConfig)returns an extended configuration.This approach is flexible, but users need to manage the order when composing multiple wrappers. See the Next.js MDX documentation.
Of these approaches,
extendsis the best fit for Rstack CLI: users only need to understand shared configurations and their order of precedence.Rspack CLI and Rstest already provide an
extendsconfiguration field, so this approach follows familiar APIs in the Rstack ecosystem.Design principles
rs mcp, is outside the scope of this RFC. If needed, we can explore an API such asdefine.command()separately.Configuration example
A developer infrastructure team can define shared configuration for builds, tests, and linting:
A project can then inherit the shared configuration in
rstack.config.tsand add its own settings:API design
define.extends()Use
define.extends()to inherit one or more shared configurations. Build plugins in shared configurations are still registered through existing fields such asapp.pluginsandlib.plugins.Like the other
define.*methods,define.extends()can be called at most once per configuration load. Calling it more than once throws an error.RstackConfigThe
RstackConfigtype describes a shared configuration. Each tool's field accepts exactly the same input as its correspondingdefinemethod:Shared configurations
A shared configuration can be a plain object:
Each field follows the same conventions as its corresponding
definemethod. For example,appaccepts the same input asdefine.app:Shared configurations can also be created by ordinary functions, allowing consumers to customize them through arguments:
Consumers can call
sharedConfig({ testEnvironment: 'node' })to create a configuration with their preferred options.Merge rules
Configurations are merged in the following order:
define.extends(), from left to right.definemethods.Each tool uses its own merge rules:
appmergeRsbuildConfig.libmergeRslibConfig.testmergeRstestConfig.lintfmtoverrides.docstagedTest configuration inheritance
Currently,
define.test()automatically inherits the application or library configuration whenextendsis not explicitly set.With shared configurations, the test configuration must first be resolved and merged across layers before deciding whether to apply automatic inheritance.
The process is:
testconfiguration from all layers.extendsandprojectsin the merged result to determine whether automatic inheritance is needed.appconfiguration. Fall back tolibonly if no layer definesapp.Path resolution
Relative paths in shared configurations continue to follow each tool's existing resolution rules. Different tools may resolve paths relative to different base directories.
For example, a shared configuration that ships its own test setup file should reference it with an absolute path:
Nested inheritance
A shared configuration can inherit other shared configurations:
Nested configurations are expanded depth-first: inherited configurations are applied before the object that extends them. In this example, the order is
baseConfig → reactConfig → sharedConfig, followed by the consuming project's own configuration registered through thedefinemethods.Considerations
import()calls inside configuration functions to load plugins and third-party packages without adding unnecessary startup overhead.All reactions