/** * RSCRspackPlugin — rspack-native equivalent of RSCWebpackPlugin. * * Emits React on Rails' existing client-manifest JSON schema using only * standard rspack public APIs — no dependency on rspack's experimental RSC * system (`rspackExperiments.reactServerComponents`, `experiments.rsc`, * `react-server-dom-rspack`). * * Discovery technique: a small loader (`loader.ts`) tags modules containing * a `"use client"` directive during parse by adding the module's resource * path to a per-compilation Set keyed under the `CLIENT_MODULES_KEY` * Symbol. A second loader prepends dynamic imports to the Flight client * runtime so file-system-discovered client references become async chunk * groups. At `processAssets`, the plugin walks chunk groups and emits the * React on Rails client-manifest JSON schema. * * Output schema matches RoR's existing webpack-side plugin so * `buildServerRenderer` / `buildClientRenderer` in server.node.ts / * client.node.ts work without changes. */ type AnyLogger = { warn(...args: unknown[]): void; info(...args: unknown[]): void; debug(...args: unknown[]): void; }; type AnyCompiler = { options: { module?: { rules?: unknown[]; }; context?: string; }; context: string; hooks: { beforeCompile: { tapAsync: (name: string, fn: (params: unknown, cb: (err?: Error | null) => void) => void) => void; }; thisCompilation: { tap: (name: string, fn: (compilation: unknown) => void) => void; }; }; rspack?: { version?: string; }; webpack?: { version?: string; }; inputFileSystem?: { readFileSync?(p: string, enc: string): string; }; resolverFactory?: { get(type: string, options?: unknown): unknown; }; getInfrastructureLogger?(name: string): AnyLogger; }; /** * A search-path descriptor matching the webpack plugin's `clientReferences` * shape. Each entry tells the plugin to walk a directory for files matching * `include` (a RegExp), optionally excluding via `exclude`. */ export type ClientReferenceSearchPath = { directory: string; recursive?: boolean; include: RegExp; exclude?: RegExp; }; export type ClientReferencePath = string | ClientReferenceSearchPath; export interface Options { /** * Whether the plugin is applied to the server bundle (as opposed to the * client bundle). Determines the default manifest filename and which * runtime module the plugin looks for the client runtime against. */ isServer: boolean; /** * Override the client manifest filename. Defaults to * `react-client-manifest.json` for client, `react-server-client-manifest.json` * for server, matching the webpack plugin's defaults. */ clientManifestFilename?: string; /** * Where to look for `"use client"` files. Each entry is either: * - A string (absolute path to a single file), or * - A search descriptor: `{ directory, recursive?, include, exclude? }` * * The plugin FS-walks each descriptor at `beforeCompile` time, reads * every matching file, checks for the `"use client"` directive, and * injects the discovered files into the bundle as named async chunks * through the Flight runtime injection loader. This ensures the client/SSR * bundle includes every client component even if nothing in the entry graph * explicitly imports it — matching the webpack plugin's behavior. * * Default: scan the compiler context for JS/TS files while excluding * dependency and generated asset directories such as `node_modules`, * `vendor/bundle`, and `vendor/cache`. */ clientReferences?: ClientReferencePath | ReadonlyArray; /** * Template for naming async chunks created for each client reference. * Supports `[index]` (sequential number) and `[request]` (sanitised * file path). Default: `"client[index]"`. */ chunkName?: string; } export declare const RSC_LOADER_RULE: { test: RegExp; exclude: RegExp; enforce: "pre"; use: { loader: string; }[]; }; export declare class RSCRspackPlugin { private readonly options; private readonly clientReferences; private readonly chunkName; constructor(options: Options); apply(compiler: AnyCompiler): void; private resolveAllClientFiles; private walkDir; private addResolvedClientFile; private normalizeResourcePath; /** * Resolves the bundler runtime namespace. Prefers `compiler.rspack` (if * present — rspack sets this), falls back to `compiler.webpack` (webpack 5 * convention), then tries `require('webpack')` as a last resort. * * This means the same plugin code works under both rspack and webpack * without an explicit bundler option, as long as the bundler exposes the * convention-standard `Compilation` and `sources` types. */ private resolveBundler; /** * Injects the tagging loader rule into compiler.options.module.rules at * position 0 (so it runs `pre` relative to user rules). Idempotent — if * the rule is already present, do nothing. */ private ensureLoaderRule; private hasLoaderRule; /** * Build the RoR-shape manifest from the tagged module set. * * Iterates `compilation.chunkGroups` (matching the webpack plugin's * pattern) so the `chunks` array for each module reflects ALL chunks * in the chunk group — not just the ones directly containing the * module. This matters for split-chunk configurations where sibling * chunks must be preloaded together. */ private buildManifest; /** Stash resolved client files so buildManifest can filter by them. */ private _resolvedClientFiles; /** Build the chunks array from all async-loadable chunks in a chunk group. */ private getGroupChunks; private getInitialChunks; private isInitialChunk; /** * Record a single module in the manifest if it is a resolved client reference. * `moduleId` and `chunks` come from the enclosing context (the chunk * group walk or the outer ConcatenatedModule). */ private recordModule; } export default RSCRspackPlugin; //# sourceMappingURL=plugin.d.ts.map