For AI agents: the complete documentation index is available at https://rsdoctor.rs/llms.txt, the full documentation bundle is available at https://rsdoctor.rs/llms-full.txt, and this page is available as Markdown at https://rsdoctor.rs/guide/rules/rule-custom.md.
close
  • English
  • Custom extension rules

    Introduction

    Considering that users may have their own specific rule definition requirements, Rsdoctor provides an external interface for users to customize their own rule checks in addition to the built-in rules. The external extension interface needs to be configured on the Rsdoctor plugin through the extends field, and its configuration is also placed in the rules field. See the example below:

    // src/rules/assets-count-limit.ts
    import { defineRule } from '@rsdoctor/core';
    
    export const AssetsCountLimit = defineRule(() => ({
      meta: {
        category: 'bundle',
        severity: 'Warn',
        title: 'assets-count-limit',
        defaultConfig: {
          limit: 10,
        },
      },
      check({ chunkGraph, report, ruleConfig }) {
        const assets = chunkGraph.getAssets();
    
        if (assets.length > ruleConfig.limit) {
          report({
            message: 'The count of assets is bigger than limit',
            detail: {
              type: 'link',
              link: 'https://rsdoctor.rs/zh/guide/start/quick-start', // This link just for show case.
            },
          });
        }
      },
    }));
    // rsbuild.config.ts
    import { RsdoctorRspackPlugin } from '@rsdoctor/core';
    import { AssetsCountLimit } from './rules/assets-count-limit';
    
    export default {
      tools: {
        bundlerChain: (chain) => {
          chain.plugin('Rsdoctor').use(RsdoctorRspackPlugin, [
            {
              linter: {
                level: 'Error',
                extends: [AssetsCountLimit],
                rules: {
                  'assets-count-limit': [
                    'on',
                    {
                      limit: 1, // rule custom configs
                    },
                  ],
                },
              },
            },
          ]);
        },
      },
    };

    You can follow the detailed steps below to define and write custom rules.

    Steps for custom rules

    1. Installation

    When writing custom rules, install @rsdoctor/core and use the defineRule function from @rsdoctor/core to define unified Rsdoctor rules.

    npm
    yarn
    pnpm
    bun
    deno
    npm add @rsdoctor/core -D

    2. Writing rules

    To write rules, you need to use the defineRule function, which takes a function as input and returns an object in a fixed format. Refer to the following example:

    // src/rules/some-rule.ts
    import { defineRule } from '@rsdoctor/core';
    
    const ruleTitle = 'check-rule';
    type RuleConfig = { limit: number };
    
    export const CheckRule = defineRule<typeof ruleTitle, RuleConfig>(() => ({
      meta: {
        category: 'bundle', // rule category
        severity: 'Warn', // rule severity
        title: ruleTitle, // rule title
        defaultConfig: { limit: 10 },
      },
      check({ chunkGraph, report, ruleConfig }) {
        if (chunkGraph.getAssets().length > ruleConfig.limit) {
          report({ message: 'Too many assets' });
        }
      },
    }));

    The meta field contains the fixed configuration and content of this rule, and the check field contains the callback that includes the specific logic for rule checking. Their types are as follows.

    meta object

    For the metadata type accepted by external rules, see ExtendRuleMeta.

    Property meanings
    • meta
      • category
        • info: Defines the category of the rule.
        • type: 'compile' | 'bundle'.
      • title
        • info: The title of the rule, used to display in the Rsdoctor report page.
        • type: string | generics, can be passed down through generics.
      • severity
        • info: The severity level of the rule.
        • type: 'Ignore' | 'Warn' | 'Error' for external rules.
        • default: 'Warn'
      • defaultConfig
        • info: The default configuration of the rule. Custom rules may require specific configurations, and defaultConfig can be used to configure the default rule configuration.
        • type: Generics, can be defined through generics. As shown in the example above.
      • referenceUrl
        • info: The documentation link for the rule.
        • type: string.

    check function

    The check function is mainly used for rule judgment. The parameter ruleContext is all the build information that Rsdoctor integrates during the build analysis process, and its type is defined as follows. You can use the build information in the body of the check function to make custom rule judgments. After the judgment, if the rule check fails, you can report it using the report method in the parameter. See the next step for details.

    CheckCallback type
    type CheckCallback<Config = DefaultRuleConfig> = (
      context: RuleCheckerContext<Config>,
    ) => void | Promise<void>;

    RuleCheckerContext type definition, please refer to the details

    Example

    The CheckRule example above checks the number of assets and reports a finding when it exceeds ruleConfig.limit.

    3. Reporting rule results

    To report errors, you need to use the report method in the check callback function's parameter. The report method's parameters mainly include the following parts:

    • message: The error message.
    • document: File data used to describe the location of the error code and code position.
    • suggestions: Rule suggestions.
    • detail: Detailed information, mainly providing additional data to the frontend.

    For detailed type definitions, refer to: ReportData

    4. Displaying rule results

    The report function will pass the error information of custom rules to the compilation's errors or warnings. It will display the rule results in the terminal during the build process, and even interrupt the build. Rsdoctor displays custom rule results in a warning list. For more details, see Display Components.

    • Basic Rule Warning Component

    Display components

    Basic rule warning component

    • Component Type

      LinkRule Type

    • Component Input

      • type

        • The type of the component.
        • value: 'link'.
      • title

        • The title of the rule.
        • type: string.
      • description

        • The description of the rule. The data comes from the message or detail.description in the report function:
          report({
            message: 'The count of assets is bigger than limit',
            detail: {
              // ......
              description: 'The count of assets is bigger than limit',
            },
          });
        • type: string.
      • level

        • The level of the rule.
        • type: warn | error.
      • link:

        • The details of the rule. The data comes from detail.link:
          report({
            detail: {
              // ......
              link: 'http://....',
            },
          });
        • type:string。
    • Example

    report({
      message: 'The count of assets is bigger than limit',
      detail: {
        type: 'link',
        link: 'https://rsdoctor.rs/zh/guide/start/quick-start', // This link just for show case.
      },
    });
    • Display Components
    • Component Code Code

    Type definitions

    ExtendRuleMeta

    interface ExtendRuleMeta<
      Config = DefaultRuleConfig,
      Title extends DefaultRuleTitle = DefaultRuleTitle,
    > {
      title: Title;
      category: 'compile' | 'bundle';
      severity: 'Ignore' | 'Warn' | 'Error';
      referenceUrl?: string;
      defaultConfig?: Config;
    }

    This is the metadata accepted by external rules. Internal rules also specify a rule code and use numeric severity values.

    RuleCheckerContext

    interface RuleCheckerContext<Config> {
      /** Project root directory */
      root: string;
      /** Current rule configuration */
      ruleConfig: Config;
      /** Project configuration */
      configs: ConfigData;
      /** Build errors */
      errors: Error[];
      /** Chunk graph */
      chunkGraph: ChunkGraph;
      /** Module graph */
      moduleGraph: ModuleGraph;
      /** Package graph */
      packageGraph: PackageGraph;
      /** Loader data */
      loader: LoaderData;
      /**
       * Report Error
       * @param {any} error - error info
       * @param {any} replacer - Replace the original error
       */
      report(error: ReportData, replacer?: any): void;
    }

    ReportData

    interface ReportData {
      /** Error message */
      message: string;
      /** File data */
      document?: ReportDocument;
      /** Diagnostic suggestions */
      suggestions?: Suggestion;
      /**
       * Detailed information
       *   - Mainly provides additional data for the frontend
       */
      detail?: any;
    }
    
    /** Error file information */
    interface ReportDocument {
      /** file path */
      path: string;
      /**  Is it a transformed code */
      isTransformed?: boolean;
      /** code content */
      content: string;
      range: Range;
    }

    LinkRuleStoreData

    interface BaseRuleStoreData extends Pick<RuleMessage, 'code' | 'category'> {
      /**
       * unique of error
       */
      id: number | string;
      /**
       * title of alerts
       */
      title: string;
      /**
       * description of alerts
       */
      description?: string;
      /**
       * level of error
       */
      level: 'warn' | 'error';
      /**
       * rule doc link
       */
      link?: string;
    }
    
    interface LinkRuleStoreData extends BaseRuleStoreData {
      type: 'link';
    }

    CodeViewRule

    interface CodeViewRuleStoreData extends BaseRuleStoreData {
      type: 'code-view';
      file: {
        /**
         * file path
         */
        path: string;
        /**
         * the code content
         */
        content: string;
        /**
         * fix highlight range in source
         */
        ranges?: SourceRange[];
      };
    }
    
    /** Source code location */
    interface SourcePosition {
      line?: number;
      column?: number;
      index?: number;
    }
    
    /** Source code range */
    interface SourceRange {
      start: SourcePosition;
      end?: SourcePosition;
    }

    Tools

    AST processing

    The rule context exposes moduleGraph. Use moduleGraph.getModules() to access modules. For AST-based checks, parse the available original code from module.getSource().source with a parser that supports your project's syntax. Skip modules without source code. The internal parser, asserts, and Document helpers are not exported by @rsdoctor/core.

    Data reporting

    Please go to Data Reporting for viewing.