@savvy-web/rslib-builder 0.18.3 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,7 @@ Build modern ESM Node.js libraries with minimal configuration. Handles TypeScrip
12
12
  - **10-100x Faster Types** - Uses tsgo (native TypeScript compiler) with API Extractor for bundled, clean public API declarations
13
13
  - **Production-Ready Transforms** - Converts `.ts` exports to `.js`, resolves PNPM `catalog:` and `workspace:` references, generates files array
14
14
  - **Bundled or Bundleless** - Choose single-file bundles per entry or bundleless mode that preserves your source file structure
15
+ - **RSPress Plugin Builder** - Dedicated builder for RSPress plugins with dual-bundle architecture (plugin + runtime) and React support
15
16
  - **Multi-Target Builds** - Separate dev (with source maps) and npm (optimized) outputs from a single configuration
16
17
  - **Flexible Formats** - ESM, CJS, or dual format output with per-entry format overrides
17
18
  - **TSDoc Validation** - Pre-build documentation validation with automatic public API discovery
@@ -35,13 +36,33 @@ export default NodeLibraryBuilder.create({
35
36
 
36
37
  Build with `rslib build --env-mode dev` or `rslib build --env-mode npm`.
37
38
 
39
+ ## RSPress Plugins
40
+
41
+ Build RSPress plugins with zero-config dual-bundle output (plugin + runtime):
42
+
43
+ ```typescript
44
+ // rslib.config.ts
45
+ import { RSPressPluginBuilder } from '@savvy-web/rslib-builder';
46
+
47
+ export default RSPressPluginBuilder.create({
48
+ plugin: {
49
+ externals: ['typescript', 'shiki'],
50
+ },
51
+ runtime: {
52
+ externals: ['@rspress/plugin-llms'],
53
+ },
54
+ });
55
+ ```
56
+
57
+ RSPressPluginBuilder auto-detects the runtime entry at `src/runtime/index.tsx`, handles React JSX compilation, CSS modules, and built-in RSPress externals. For advanced configuration, see [RSPress Plugin Setup](../docs/guides/rspress-plugins.md).
58
+
38
59
  ## Documentation
39
60
 
40
- For configuration options, API reference, and advanced usage, see [docs](./docs/).
61
+ For configuration options, API reference, and advanced usage, see [docs](../docs/).
41
62
 
42
63
  ## Contributing
43
64
 
44
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
65
+ See [CONTRIBUTING.md](../CONTRIBUTING.md) for development setup and guidelines.
45
66
 
46
67
  ## License
47
68
 
package/index.d.ts CHANGED
@@ -53,6 +53,7 @@ import type { RslibConfig } from '@rslib/core';
53
53
  import { ScriptTarget } from 'typescript';
54
54
  import type { SourceConfig } from '@rsbuild/core';
55
55
  import ts from 'typescript';
56
+ import type { TSConfigJsonWithSchema } from '../types/tsconfig-json.js';
56
57
 
57
58
  /**
58
59
  * Options for API model generation.
@@ -454,6 +455,18 @@ export declare interface DtsPluginOptions {
454
455
  * Metadata files (api.json, tsdoc-metadata.json, tsconfig.json) are NOT prefixed.
455
456
  */
456
457
  dtsPathPrefix?: string;
458
+ /** Override the default LibraryTSConfigFile used for temp tsconfig generation. */
459
+ tsconfigPreset?: LibraryTSConfigFile;
460
+ /**
461
+ * Explicitly provide entry points instead of discovering from package.json exports.
462
+ * Map of entry name to source path (e.g., `{ index: "./src/index.ts" }`).
463
+ *
464
+ * @remarks
465
+ * Used by RSPressPluginBuilder to prevent cross-contamination between the plugin lib
466
+ * and runtime lib — each lib's DtsPlugin only processes its own entries.
467
+ * When set, the EntryExtractor is bypassed entirely.
468
+ */
469
+ overrideEntries?: Record<string, string>;
457
470
  }
458
471
 
459
472
  /**
@@ -1115,6 +1128,38 @@ export declare type JsonValue = JsonPrimitive | JsonObject | JsonArray;
1115
1128
  */
1116
1129
  export declare type LibraryFormat = "esm" | "cjs";
1117
1130
 
1131
+ /**
1132
+ * Represents a TypeScript configuration file for library builds.
1133
+ *
1134
+ * @remarks
1135
+ * This class extends {@link TSConfigFile} to add library-specific build methods for
1136
+ * generating bundled build configurations. It provides methods to:
1137
+ * - Generate build-mode specific configurations
1138
+ * - Write temporary configuration files for build tools
1139
+ * - Transform paths and filter includes based on build mode
1140
+ *
1141
+ * @see {@link TSConfigFile} for the base class
1142
+ * @internal
1143
+ */
1144
+ export declare class LibraryTSConfigFile extends TSConfigFile {
1145
+ /**
1146
+ * Write the bundle-mode configuration to a temporary file.
1147
+ *
1148
+ * @remarks
1149
+ * Creates a temporary tsconfig.json file with absolute paths that work from /tmp.
1150
+ * Uses the base compiler options from the template but generates include patterns
1151
+ * dynamically based on the actual package structure.
1152
+ *
1153
+ * The temporary file will be automatically cleaned up when the process exits.
1154
+ *
1155
+ * @param mode - Build mode (dev, npm)
1156
+ * @returns Absolute path to the temporary file
1157
+ */
1158
+ writeBundleTempConfig(_mode: "dev" | "npm", options?: {
1159
+ types?: string[];
1160
+ }): string;
1161
+ }
1162
+
1118
1163
  /**
1119
1164
  * Allows creating a union type by combining primitive types and literal types
1120
1165
  * without sacrificing auto-completion in IDEs for the literal type part of the union.
@@ -2356,6 +2401,95 @@ export declare function resolvePublishTargets(packageJson: PackageJson, cwd: str
2356
2401
  */
2357
2402
  export declare type RslibConfigAsyncFn = (env: ConfigParams) => Promise<RslibConfig>;
2358
2403
 
2404
+ /** Built-in externals for RSPress plugin bundles. */
2405
+ export declare const RSPRESS_PLUGIN_EXTERNALS: readonly string[];
2406
+
2407
+ /** Built-in externals for RSPress runtime bundles. */
2408
+ export declare const RSPRESS_RUNTIME_EXTERNALS: readonly string[];
2409
+
2410
+ /**
2411
+ * Builder for RSPress plugin packages with dual-bundle architecture.
2412
+ *
2413
+ * @remarks
2414
+ * Produces up to two RSlib lib configs:
2415
+ * - **Plugin lib**: The main RSPress plugin entry (always generated)
2416
+ * - **Runtime lib**: React components for the RSPress theme (auto-detected or explicit)
2417
+ *
2418
+ * @public
2419
+ */
2420
+ export declare class RSPressPluginBuilder {
2421
+ private static readonly VALID_MODES;
2422
+ /**
2423
+ * Merges user-provided options with defaults.
2424
+ *
2425
+ * @param options - Partial configuration options to merge
2426
+ * @returns Merged options with defaults applied
2427
+ */
2428
+ static mergeOptions(options?: Partial<RSPressPluginBuilderOptions>): RSPressPluginBuilderOptions;
2429
+ /**
2430
+ * Creates an async RSLib configuration function for RSPress plugin packages.
2431
+ *
2432
+ * @param options - Configuration options for the builder
2433
+ * @returns An async function compatible with RSLib's config system
2434
+ */
2435
+ static create(options?: Partial<RSPressPluginBuilderOptions>): RslibConfigAsyncFn;
2436
+ /**
2437
+ * Creates a single-mode build configuration with plugin and optional runtime libs.
2438
+ *
2439
+ * @param mode - The build mode ("dev" or "npm")
2440
+ * @param options - Configuration options
2441
+ * @returns Promise resolving to the RSLib configuration
2442
+ */
2443
+ static createSingleMode(mode: BuildMode, options: RSPressPluginBuilderOptions): Promise<RslibConfig>;
2444
+ /**
2445
+ * Determines whether the runtime bundle should be enabled.
2446
+ */
2447
+ private static shouldEnableRuntime;
2448
+ /**
2449
+ * Creates the plugin lib configuration.
2450
+ */
2451
+ private static createPluginLib;
2452
+ private static createRuntimeLib;
2453
+ }
2454
+
2455
+ /**
2456
+ * Options for RSPressPluginBuilder.
2457
+ * @public
2458
+ */
2459
+ export declare interface RSPressPluginBuilderOptions {
2460
+ /** Plugin bundle configuration. Always generated. */
2461
+ plugin?: RSPressPluginBundleOptions;
2462
+ /** Runtime bundle configuration. Auto-detected from `src/runtime/index.tsx`, or `false` to disable. */
2463
+ runtime?: RSPressPluginBundleOptions | false;
2464
+ /** API model generation options. Default: `true`. */
2465
+ apiModel?: ApiModelOptions | boolean;
2466
+ /** Packages to bundle in TypeScript declarations. */
2467
+ dtsBundledPackages?: string[];
2468
+ /** Custom package.json transform function. */
2469
+ transform?: TransformPackageJsonFn;
2470
+ /** Path to custom tsconfig.json. Overrides the default RSPress plugin preset. */
2471
+ tsconfigPath?: string;
2472
+ /** Build modes to produce. Default: `["dev", "npm"]`. */
2473
+ modes?: BuildMode[];
2474
+ /** Static files to copy to output (e.g., i18n JSON). */
2475
+ copyPatterns?: (string | CopyPatternConfig)[];
2476
+ }
2477
+
2478
+ /**
2479
+ * Options for an individual RSPress plugin bundle (plugin or runtime).
2480
+ * @public
2481
+ */
2482
+ export declare interface RSPressPluginBundleOptions {
2483
+ /** Entry point path. Plugin default: `"./src/index.ts"`, runtime default: `"./src/runtime/index.tsx"` */
2484
+ entry?: string;
2485
+ /** Additional externals merged with built-in RSPress externals. */
2486
+ externals?: (string | RegExp)[];
2487
+ /** Additional Rsbuild plugins. `pluginReact()` is auto-added on runtime. */
2488
+ plugins?: RsbuildPlugin[];
2489
+ /** Additional define constants merged with `{ "import.meta.env": "import.meta.env" }`. */
2490
+ define?: SourceConfig["define"];
2491
+ }
2492
+
2359
2493
  /**
2360
2494
  * Function to transform package.json during the build process.
2361
2495
  *
@@ -2393,6 +2527,103 @@ export declare type TransformPackageJsonFn = (context: {
2393
2527
  pkg: PackageJson;
2394
2528
  }) => PackageJson;
2395
2529
 
2530
+ /**
2531
+ * Represents a TypeScript configuration file with utilities for path resolution.
2532
+ *
2533
+ * @remarks
2534
+ * This class provides convenient access to TypeScript configuration files,
2535
+ * with automatic path resolution and JSON parsing. It implements custom
2536
+ * inspection for improved debugging output in Node.js console.
2537
+ *
2538
+ * The class handles workspace-aware path resolution that correctly handles
2539
+ * cross-package references by using node_modules symlinks when crossing
2540
+ * package boundaries.
2541
+ *
2542
+ * The class uses lazy getters for both path resolution and configuration
2543
+ * parsing, ensuring that file I/O only occurs when these properties are
2544
+ * accessed. This makes it efficient to create instances without immediately
2545
+ * reading from disk.
2546
+ *
2547
+ * This base class is suitable for workspace-level configurations. For library
2548
+ * build configurations that require bundle transformations, use
2549
+ * {@link LibraryTSConfigFile}.
2550
+ *
2551
+ * @see {@link LibraryTSConfigFile} for library build configurations
2552
+ * @internal
2553
+ */
2554
+ export declare class TSConfigFile {
2555
+ pathname: string;
2556
+ /**
2557
+ * Human-readable description of what this configuration is used for.
2558
+ *
2559
+ * @remarks
2560
+ * This description helps developers understand the purpose and intended use
2561
+ * case for this particular TypeScript configuration.
2562
+ */
2563
+ readonly description: string;
2564
+ /**
2565
+ * Relative path from current working directory to the config file.
2566
+ *
2567
+ * @remarks
2568
+ * Returns a relative path string prefixed with `./` that points from the
2569
+ * current working directory to the TypeScript configuration file. This is
2570
+ * useful for displaying user-friendly paths in logs and error messages.
2571
+ *
2572
+ * The path is computed lazily each time it's accessed, so it will reflect
2573
+ * changes if the current working directory changes during execution.
2574
+ *
2575
+ * @returns A relative path string with `./` prefix (e.g., `./tsconfig/ecma/lib.json`)
2576
+ */
2577
+ get path(): string;
2578
+ /**
2579
+ * Get configuration with `$\{configDir\}` variables replaced.
2580
+ *
2581
+ * @remarks
2582
+ * Returns the configuration with all `${configDir}` template variables replaced
2583
+ * with the relative path to the package root. This is useful for configurations
2584
+ * that use template variables for path resolution.
2585
+ *
2586
+ * @returns The configuration with transformed paths
2587
+ */
2588
+ get bundled(): TSConfigJsonWithSchema;
2589
+ /**
2590
+ * Parsed TypeScript configuration object.
2591
+ *
2592
+ * @remarks
2593
+ * Returns the TypeScript configuration object imported at build time.
2594
+ * The configuration object includes standard TypeScript compiler options
2595
+ * and other tsconfig.json fields with JSON schema validation.
2596
+ *
2597
+ * @returns The parsed TypeScript configuration as a {@link TSConfigJsonWithSchema} object
2598
+ *
2599
+ * @throws If the configuration file is not found in imports
2600
+ *
2601
+ * @see {@link TSConfigJsonWithSchema} for the configuration type definition
2602
+ */
2603
+ get config(): TSConfigJsonWithSchema;
2604
+ /**
2605
+ * Creates a new TSConfigFile instance.
2606
+ *
2607
+ * @remarks
2608
+ * Initializes a TSConfigFile with the specified description and absolute path
2609
+ * to a TypeScript configuration file. The constructor sets up lazy getters for
2610
+ * the `path` and `config` properties using `Object.defineProperty`, making
2611
+ * them enumerable but computed on demand.
2612
+ *
2613
+ * Additionally, it configures custom Node.js inspection behavior via
2614
+ * `util.inspect.custom` to provide pretty-printed output when the
2615
+ * instance is logged to the console. This includes showing all nested
2616
+ * levels, colors, and unlimited array lengths for better debugging.
2617
+ *
2618
+ * No file I/O occurs during construction - files are only read when
2619
+ * the `config` getter is accessed.
2620
+ *
2621
+ * @param description - Human-readable description of what this configuration is used for
2622
+ * @param pathname - Absolute path to the TypeScript configuration file
2623
+ */
2624
+ constructor(description: string, pathname: string);
2625
+ }
2626
+
2396
2627
  /**
2397
2628
  * Resolves TypeScript ParsedCommandLine to a JSON-serializable tsconfig format.
2398
2629
  *
package/index.js CHANGED
@@ -494,11 +494,21 @@ const AutoEntryPlugin = (options)=>{
494
494
  };
495
495
  };
496
496
  var lib_namespaceObject = JSON.parse('{"$schema":"https://json.schemastore.org/tsconfig.json","compilerOptions":{"allowSyntheticDefaultImports":true,"composite":true,"declaration":true,"declarationDir":"${configDir}/dist","declarationMap":false,"emitDeclarationOnly":false,"esModuleInterop":true,"explainFiles":false,"forceConsistentCasingInFileNames":true,"incremental":true,"isolatedDeclarations":false,"isolatedModules":true,"exactOptionalPropertyTypes":true,"jsx":"preserve","lib":["esnext"],"module":"nodenext","moduleResolution":"nodenext","outDir":"${configDir}/dist","resolveJsonModule":true,"rootDir":"${configDir}","skipLibCheck":true,"sourceMap":false,"strict":true,"strictNullChecks":true,"target":"es2023","tsBuildInfoFile":"${configDir}/dist/.tsbuildinfo.lib","typeRoots":["${configDir}/node_modules/@types","${configDir}/types"],"verbatimModuleSyntax":true},"exclude":["${configDir}/node_modules","${configDir}/dist/**/*"],"include":["${configDir}/types/*.ts","${configDir}/package.json","${configDir}/*.ts","${configDir}/*.cts","${configDir}/*.mts","${configDir}/src/**/*.ts","${configDir}/src/**/*.tsx","${configDir}/src/**/*.cts","${configDir}/src/**/*.mts","${configDir}/lib/**/*.ts","${configDir}/lib/**/*.tsx","${configDir}/lib/**/*.cts","${configDir}/lib/**/*.mts","${configDir}/__test__/**/*.ts","${configDir}/__test__/**/*.tsx","${configDir}/__test__/**/*.cts","${configDir}/__test__/**/*.mts","${configDir}/public/**/*.json"]}');
497
+ var plugin_namespaceObject = JSON.parse('{"$schema":"https://json.schemastore.org/tsconfig.json","compilerOptions":{"composite":true,"declaration":true,"exactOptionalPropertyTypes":true,"jsx":"react-jsx","module":"esnext","moduleResolution":"bundler","resolveJsonModule":true,"skipLibCheck":true,"strict":true,"strictNullChecks":true,"target":"es2023"},"exclude":["${configDir}/node_modules","${configDir}/dist","${configDir}/.rslib"],"include":["${configDir}/src/**/*.ts","${configDir}/src/**/*.tsx","${configDir}/src/**/*.mts","${configDir}/src/**/*.cts","${configDir}/src/**/*.mdx","${configDir}/types/**/*.ts"]}');
498
+ var website_namespaceObject = JSON.parse('{"$schema":"https://json.schemastore.org/tsconfig.json","compilerOptions":{"exactOptionalPropertyTypes":true,"jsx":"react-jsx","lib":["DOM","ES2023"],"module":"esnext","moduleResolution":"bundler","resolveJsonModule":true,"skipLibCheck":true,"strict":true,"strictNullChecks":true,"target":"es2023"},"exclude":["${configDir}/node_modules","${configDir}/dist","${configDir}/.rspress","${configDir}/.rslib"],"include":["${configDir}/*.ts","${configDir}/*.mts","${configDir}/*.cts","${configDir}/**/*.tsx","${configDir}/docs/**/*.mdx"]}');
497
499
  const requireCJS = createRequire(import.meta.url);
498
500
  const jsonImports = new Map([
499
501
  [
500
502
  join(import.meta.dirname, "../public/tsconfig/ecma/lib.json"),
501
503
  lib_namespaceObject
504
+ ],
505
+ [
506
+ join(import.meta.dirname, "../public/tsconfig/rspress/plugin.json"),
507
+ plugin_namespaceObject
508
+ ],
509
+ [
510
+ join(import.meta.dirname, "../public/tsconfig/rspress/website.json"),
511
+ website_namespaceObject
502
512
  ]
503
513
  ]);
504
514
  function transformStringsDeep(value, transform) {
@@ -593,11 +603,17 @@ class LibraryTSConfigFile extends TSConfigFile {
593
603
  }
594
604
  }
595
605
  const NodeEcmaLib = new LibraryTSConfigFile("ECMAScript library build configuration", join(import.meta.dirname, "../public/tsconfig/ecma/lib.json"));
606
+ const RSPressPlugin = new LibraryTSConfigFile("RSPress plugin build configuration", join(import.meta.dirname, "../public/tsconfig/rspress/plugin.json"));
607
+ const RSPressWebsite = new TSConfigFile("RSPress website configuration", join(import.meta.dirname, "../public/tsconfig/rspress/website.json"));
596
608
  const TSConfigs = {
597
609
  node: {
598
610
  ecma: {
599
611
  lib: NodeEcmaLib
600
612
  }
613
+ },
614
+ rspress: {
615
+ plugin: RSPressPlugin,
616
+ website: RSPressWebsite
601
617
  }
602
618
  };
603
619
  function compileRule(rule) {
@@ -1185,9 +1201,9 @@ async function bundleDtsFiles(options) {
1185
1201
  }
1186
1202
  }
1187
1203
  const dtsExtension = "cjs" === options.format ? ".d.cts" : ".d.ts";
1204
+ const isMainEntry = "index" === entryName || 1 === entryPoints.size;
1188
1205
  const outputFileName = `${entryName}${dtsExtension}`;
1189
1206
  const tempBundledPath = join(tempOutputDir, outputFileName);
1190
- const isMainEntry = "index" === entryName || 1 === entryPoints.size;
1191
1207
  const generateApiModel = apiModelEnabled;
1192
1208
  const tempApiModelPath = generateApiModel ? join(tempOutputDir, `${entryName}.api.json`) : void 0;
1193
1209
  const generateTsdocMetadata = tsdocMetadataEnabled && isMainEntry;
@@ -1465,7 +1481,8 @@ function runTsgo(options) {
1465
1481
  process.chdir(cwd);
1466
1482
  const projectConfigPath = findTsConfig(cwd);
1467
1483
  const projectTypes = projectConfigPath ? loadTsConfig(projectConfigPath).options.types : void 0;
1468
- configTsconfigPath = TSConfigs.node.ecma.lib.writeBundleTempConfig(options.buildMode, projectTypes ? {
1484
+ const preset = options.tsconfigPreset ?? TSConfigs.node.ecma.lib;
1485
+ configTsconfigPath = preset.writeBundleTempConfig(options.buildMode, projectTypes ? {
1469
1486
  types: projectTypes
1470
1487
  } : void 0);
1471
1488
  } finally{
@@ -1573,28 +1590,34 @@ function runTsgo(options) {
1573
1590
  const packageJsonContent = await readFile(packageJsonPath, "utf-8");
1574
1591
  packageJson = JSON.parse(packageJsonContent);
1575
1592
  }
1576
- const { entries, exportPaths } = new EntryExtractor().extract(packageJson);
1577
1593
  const entryPoints = new Map();
1578
- const virtualEntryNames = api.useExposed("virtual-entry-names");
1579
- for (const [entryName, sourcePath] of Object.entries(entries)){
1580
- if (entryName.startsWith("bin/")) continue;
1581
- if (virtualEntryNames?.has(entryName)) {
1582
- log.global.info(`Skipping type generation for virtual entry: ${entryName}`);
1583
- continue;
1584
- }
1585
- if (!sourcePath.match(/\.(ts|mts|cts|tsx)$/)) continue;
1586
- if (sourcePath.includes(".test.") || sourcePath.includes("__test__")) continue;
1587
- const resolvedSourcePath = sourcePath.startsWith(".") ? join(cwd, sourcePath) : sourcePath;
1588
- if (!resolvedSourcePath.startsWith(cwd)) {
1589
- log.global.info(`Skipping export "${entryName}" (source outside package: ${sourcePath})`);
1590
- continue;
1591
- }
1592
- let finalSourcePath = sourcePath;
1593
- if (apiExtractorMapping && resolvedSourcePath === apiExtractorMapping.tempPath) {
1594
- finalSourcePath = apiExtractorMapping.originalPath;
1595
- log.global.info(`Using original path for api-extractor: ${finalSourcePath}`);
1594
+ let exportPaths = {};
1595
+ if (options.overrideEntries) for (const [entryName, sourcePath] of Object.entries(options.overrideEntries))entryPoints.set(entryName, sourcePath);
1596
+ else {
1597
+ const extracted = new EntryExtractor().extract(packageJson);
1598
+ const entries = extracted.entries;
1599
+ exportPaths = extracted.exportPaths;
1600
+ const virtualEntryNames = api.useExposed("virtual-entry-names");
1601
+ for (const [entryName, sourcePath] of Object.entries(entries)){
1602
+ if (entryName.startsWith("bin/")) continue;
1603
+ if (virtualEntryNames?.has(entryName)) {
1604
+ log.global.info(`Skipping type generation for virtual entry: ${entryName}`);
1605
+ continue;
1606
+ }
1607
+ if (!sourcePath.match(/\.(ts|mts|cts|tsx)$/)) continue;
1608
+ if (sourcePath.includes(".test.") || sourcePath.includes("__test__")) continue;
1609
+ const resolvedSourcePath = sourcePath.startsWith(".") ? join(cwd, sourcePath) : sourcePath;
1610
+ if (!resolvedSourcePath.startsWith(cwd)) {
1611
+ log.global.info(`Skipping export "${entryName}" (source outside package: ${sourcePath})`);
1612
+ continue;
1613
+ }
1614
+ let finalSourcePath = sourcePath;
1615
+ if (apiExtractorMapping && resolvedSourcePath === apiExtractorMapping.tempPath) {
1616
+ finalSourcePath = apiExtractorMapping.originalPath;
1617
+ log.global.info(`Using original path for api-extractor: ${finalSourcePath}`);
1618
+ }
1619
+ entryPoints.set(entryName, finalSourcePath);
1596
1620
  }
1597
- entryPoints.set(entryName, finalSourcePath);
1598
1621
  }
1599
1622
  if (0 === entryPoints.size) log.global.warn("No TypeScript exports found in package.json, skipping declaration bundling");
1600
1623
  else {
@@ -3341,4 +3364,281 @@ function resolvePublishTargets(packageJson, cwd, outdir) {
3341
3364
  };
3342
3365
  });
3343
3366
  }
3344
- export { AutoEntryPlugin, DtsPlugin, EntryExtractor, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, PublishTargetPlugin, TsDocConfigBuilder, TsDocLintPlugin, TsconfigResolver, TsconfigResolverError, VirtualEntryPlugin, resolvePublishTargets };
3367
+ const RSPRESS_PLUGIN_EXTERNALS = [
3368
+ "@rspress/core"
3369
+ ];
3370
+ const RSPRESS_RUNTIME_EXTERNALS = [
3371
+ "react",
3372
+ "react/jsx-runtime",
3373
+ "react/jsx-dev-runtime",
3374
+ "@rspress/core",
3375
+ "@theme"
3376
+ ];
3377
+ const DEFAULT_RUNTIME_ENTRY = "./src/runtime/index.tsx";
3378
+ /* v8 ignore next -- @preserve */ class RSPressPluginBuilder {
3379
+ static VALID_MODES = [
3380
+ "dev",
3381
+ "npm"
3382
+ ];
3383
+ static mergeOptions(options = {}) {
3384
+ return {
3385
+ ...void 0 !== options.plugin && {
3386
+ plugin: options.plugin
3387
+ },
3388
+ ...void 0 !== options.runtime && {
3389
+ runtime: options.runtime
3390
+ },
3391
+ apiModel: options.apiModel ?? true,
3392
+ modes: options.modes ?? [
3393
+ "dev",
3394
+ "npm"
3395
+ ],
3396
+ copyPatterns: options.copyPatterns ?? [],
3397
+ ...void 0 !== options.dtsBundledPackages && {
3398
+ dtsBundledPackages: options.dtsBundledPackages
3399
+ },
3400
+ ...void 0 !== options.transform && {
3401
+ transform: options.transform
3402
+ },
3403
+ ...void 0 !== options.tsconfigPath && {
3404
+ tsconfigPath: options.tsconfigPath
3405
+ }
3406
+ };
3407
+ }
3408
+ static create(options = {}) {
3409
+ const mergedOptions = RSPressPluginBuilder.mergeOptions(options);
3410
+ return async ({ envMode })=>{
3411
+ const mode = envMode || "dev";
3412
+ const validModes = mergedOptions.modes ?? RSPressPluginBuilder.VALID_MODES;
3413
+ if (!validModes.includes(mode)) throw new Error(`Invalid env-mode: "${mode}". Must be one of: ${validModes.join(", ")}\nExample: rslib build --env-mode npm`);
3414
+ return RSPressPluginBuilder.createSingleMode(mode, mergedOptions);
3415
+ };
3416
+ }
3417
+ static async createSingleMode(mode, options) {
3418
+ const cwd = process.cwd();
3419
+ const packageJsonPath = join(cwd, "package.json");
3420
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
3421
+ const runtimeEnabled = RSPressPluginBuilder.shouldEnableRuntime(options, cwd);
3422
+ const baseOutputDir = `dist/${mode}`;
3423
+ const publishTargets = "npm" === mode ? resolvePublishTargets(packageJson, cwd, external_node_path_resolve(cwd, baseOutputDir)) : [];
3424
+ const hasTargets = publishTargets.length > 0;
3425
+ const libConfigs = [];
3426
+ libConfigs.push(RSPressPluginBuilder.createPluginLib(mode, options, hasTargets, publishTargets));
3427
+ if (runtimeEnabled) {
3428
+ const runtimeOpts = "object" == typeof options.runtime ? options.runtime : {};
3429
+ const runtimeLib = await RSPressPluginBuilder.createRuntimeLib(mode, runtimeOpts, options);
3430
+ libConfigs.push(runtimeLib);
3431
+ }
3432
+ return defineConfig({
3433
+ lib: libConfigs,
3434
+ ...options.tsconfigPath && {
3435
+ source: {
3436
+ tsconfigPath: options.tsconfigPath
3437
+ }
3438
+ },
3439
+ performance: {
3440
+ buildCache: {
3441
+ cacheDirectory: `.rslib/cache/${mode}`
3442
+ }
3443
+ }
3444
+ });
3445
+ }
3446
+ static shouldEnableRuntime(options, cwd) {
3447
+ if (false === options.runtime) return false;
3448
+ if ("object" == typeof options.runtime) return true;
3449
+ return existsSync(join(cwd, "src/runtime/index.tsx"));
3450
+ }
3451
+ static createPluginLib(mode, options, hasTargets, publishTargets) {
3452
+ const pluginOpts = options.plugin ?? {};
3453
+ const sourceMap = "dev" === mode;
3454
+ const baseOutputDir = `dist/${mode}`;
3455
+ const plugins = [];
3456
+ const apiModelConfig = "object" == typeof options.apiModel ? options.apiModel : {};
3457
+ const tsdocConfig = apiModelConfig.tsdoc;
3458
+ const lintConfig = tsdocConfig?.lint;
3459
+ const lintEnabled = false !== options.apiModel && false !== lintConfig;
3460
+ if (lintEnabled) {
3461
+ const { lint: _lint, ...tsdocWithoutLint } = tsdocConfig ?? {};
3462
+ const lintOptions = {
3463
+ ...tsdocConfig && Object.keys(tsdocWithoutLint).length > 0 && {
3464
+ tsdoc: tsdocWithoutLint
3465
+ },
3466
+ ..."object" == typeof lintConfig ? lintConfig : {}
3467
+ };
3468
+ plugins.push(TsDocLintPlugin(lintOptions));
3469
+ }
3470
+ const userTransform = options.transform;
3471
+ const transformFn = !hasTargets && userTransform ? (pkg)=>userTransform({
3472
+ mode,
3473
+ target: void 0,
3474
+ pkg
3475
+ }) : void 0;
3476
+ plugins.push(PackageJsonTransformPlugin({
3477
+ forcePrivate: "dev" === mode,
3478
+ bundle: false,
3479
+ mode,
3480
+ format: "esm",
3481
+ ...transformFn && {
3482
+ transform: transformFn
3483
+ }
3484
+ }));
3485
+ plugins.push(FilesArrayPlugin({
3486
+ mode
3487
+ }));
3488
+ plugins.push(DtsPlugin({
3489
+ ...options.tsconfigPath ? {
3490
+ tsconfigPath: options.tsconfigPath
3491
+ } : {
3492
+ tsconfigPreset: TSConfigs.rspress.plugin
3493
+ },
3494
+ abortOnError: true,
3495
+ bundle: true,
3496
+ ...options.dtsBundledPackages && {
3497
+ bundledPackages: options.dtsBundledPackages
3498
+ },
3499
+ buildMode: mode,
3500
+ format: "esm",
3501
+ ...void 0 !== options.apiModel && {
3502
+ apiModel: options.apiModel
3503
+ },
3504
+ overrideEntries: {
3505
+ index: pluginOpts.entry ?? "./src/index.ts"
3506
+ }
3507
+ }));
3508
+ if (pluginOpts.plugins) plugins.push(...pluginOpts.plugins);
3509
+ if (hasTargets) {
3510
+ const cwd = process.cwd();
3511
+ plugins.push(PublishTargetPlugin({
3512
+ targets: publishTargets,
3513
+ stagingDir: external_node_path_resolve(cwd, baseOutputDir),
3514
+ mode,
3515
+ ...userTransform && {
3516
+ transform: userTransform
3517
+ }
3518
+ }));
3519
+ }
3520
+ const externals = [
3521
+ ...RSPRESS_PLUGIN_EXTERNALS,
3522
+ ...pluginOpts.externals ?? []
3523
+ ];
3524
+ const sourceDefine = {
3525
+ "import.meta.env": "import.meta.env",
3526
+ ...pluginOpts.define
3527
+ };
3528
+ return {
3529
+ id: `${mode}-plugin`,
3530
+ output: {
3531
+ target: "node",
3532
+ module: true,
3533
+ cleanDistPath: true,
3534
+ sourceMap,
3535
+ distPath: {
3536
+ root: baseOutputDir
3537
+ },
3538
+ copy: {
3539
+ patterns: options.copyPatterns ?? []
3540
+ },
3541
+ externals
3542
+ },
3543
+ format: "esm",
3544
+ bundle: true,
3545
+ experiments: {
3546
+ advancedEsm: true
3547
+ },
3548
+ plugins,
3549
+ source: {
3550
+ ...options.tsconfigPath && {
3551
+ tsconfigPath: options.tsconfigPath
3552
+ },
3553
+ entry: {
3554
+ index: pluginOpts.entry ?? "./src/index.ts"
3555
+ },
3556
+ define: sourceDefine
3557
+ }
3558
+ };
3559
+ }
3560
+ static async createRuntimeLib(mode, runtimeOpts, options) {
3561
+ let pluginReact;
3562
+ try {
3563
+ const reactPlugin = await import("@rsbuild/plugin-react");
3564
+ pluginReact = reactPlugin.pluginReact;
3565
+ } catch (err) {
3566
+ const code = err.code;
3567
+ if ("MODULE_NOT_FOUND" === code || "ERR_MODULE_NOT_FOUND" === code) throw new Error("@rsbuild/plugin-react is required for RSPress runtime bundles. Install it as a dev dependency: pnpm add -D @rsbuild/plugin-react");
3568
+ throw err;
3569
+ }
3570
+ const sourceMap = "dev" === mode;
3571
+ const runtimeOutputDir = `dist/${mode}/runtime`;
3572
+ const plugins = [];
3573
+ plugins.push(DtsPlugin({
3574
+ ...options.tsconfigPath ? {
3575
+ tsconfigPath: options.tsconfigPath
3576
+ } : {
3577
+ tsconfigPreset: TSConfigs.rspress.plugin
3578
+ },
3579
+ abortOnError: true,
3580
+ bundle: true,
3581
+ ...options.dtsBundledPackages && {
3582
+ bundledPackages: options.dtsBundledPackages
3583
+ },
3584
+ buildMode: mode,
3585
+ format: "esm",
3586
+ apiModel: false,
3587
+ overrideEntries: {
3588
+ index: runtimeOpts.entry ?? DEFAULT_RUNTIME_ENTRY
3589
+ }
3590
+ }));
3591
+ plugins.push(pluginReact());
3592
+ if (runtimeOpts.plugins) plugins.push(...runtimeOpts.plugins);
3593
+ const externals = [
3594
+ ...RSPRESS_RUNTIME_EXTERNALS,
3595
+ ...runtimeOpts.externals ?? []
3596
+ ];
3597
+ const sourceDefine = {
3598
+ "import.meta.env": "import.meta.env",
3599
+ ...runtimeOpts.define
3600
+ };
3601
+ return {
3602
+ id: `${mode}-runtime`,
3603
+ output: {
3604
+ target: "web",
3605
+ cleanDistPath: false,
3606
+ sourceMap,
3607
+ distPath: {
3608
+ root: runtimeOutputDir
3609
+ },
3610
+ externals,
3611
+ cssModules: {
3612
+ namedExport: false,
3613
+ exportLocalsConvention: "camelCaseOnly"
3614
+ }
3615
+ },
3616
+ format: "esm",
3617
+ bundle: true,
3618
+ experiments: {
3619
+ advancedEsm: true
3620
+ },
3621
+ plugins,
3622
+ source: {
3623
+ ...options.tsconfigPath && {
3624
+ tsconfigPath: options.tsconfigPath
3625
+ },
3626
+ entry: {
3627
+ index: runtimeOpts.entry ?? DEFAULT_RUNTIME_ENTRY
3628
+ },
3629
+ define: sourceDefine
3630
+ },
3631
+ tools: {
3632
+ rspack (config, { rspack }) {
3633
+ config.plugins ??= [];
3634
+ config.plugins.push(new rspack.BannerPlugin({
3635
+ banner: 'import "./index.css";',
3636
+ raw: true,
3637
+ include: /index\.js$/
3638
+ }));
3639
+ }
3640
+ }
3641
+ };
3642
+ }
3643
+ }
3644
+ export { AutoEntryPlugin, DtsPlugin, EntryExtractor, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, PublishTargetPlugin, RSPRESS_PLUGIN_EXTERNALS, RSPRESS_RUNTIME_EXTERNALS, RSPressPluginBuilder, TsDocConfigBuilder, TsDocLintPlugin, TsconfigResolver, TsconfigResolverError, VirtualEntryPlugin, resolvePublishTargets };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/rslib-builder",
3
- "version": "0.18.3",
3
+ "version": "0.19.0",
4
4
  "private": false,
5
5
  "description": "RSlib-based build system for Node.js libraries with automatic package.json transformation, TypeScript declaration bundling, and multi-target support",
6
6
  "keywords": [
@@ -25,7 +25,8 @@
25
25
  },
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "git+https://github.com/savvy-web/rslib-builder.git"
28
+ "url": "git+https://github.com/savvy-web/rslib-builder.git",
29
+ "directory": "package"
29
30
  },
30
31
  "license": "MIT",
31
32
  "author": {
@@ -40,7 +41,9 @@
40
41
  "import": "./index.js"
41
42
  },
42
43
  "./tsconfig/root.json": "./tsconfig/root.json",
43
- "./tsconfig/ecma/lib.json": "./tsconfig/ecma/lib.json"
44
+ "./tsconfig/ecma/lib.json": "./tsconfig/ecma/lib.json",
45
+ "./tsconfig/rspress/plugin.json": "./tsconfig/rspress/plugin.json",
46
+ "./tsconfig/rspress/website.json": "./tsconfig/rspress/website.json"
44
47
  },
45
48
  "dependencies": {
46
49
  "@microsoft/tsdoc": "^0.16.0",
@@ -62,31 +65,44 @@
62
65
  },
63
66
  "peerDependencies": {
64
67
  "@microsoft/api-extractor": "^7.57.7",
68
+ "@rsbuild/plugin-react": "^1.4.0",
65
69
  "@rslib/core": "^0.20.0",
66
70
  "@types/node": "^25.2.0",
71
+ "@types/react": "^19.0.0",
67
72
  "@typescript/native-preview": "^7.0.0-dev.20260124.1",
73
+ "react": "^19.0.0",
74
+ "react-dom": "^19.0.0",
68
75
  "typescript": "^5.9.3"
69
76
  },
70
77
  "peerDependenciesMeta": {
71
78
  "@microsoft/api-extractor": {
72
79
  "optional": false
73
80
  },
81
+ "@rsbuild/plugin-react": {
82
+ "optional": true
83
+ },
74
84
  "@rslib/core": {
75
85
  "optional": false
76
86
  },
77
87
  "@types/node": {
78
88
  "optional": false
79
89
  },
90
+ "@types/react": {
91
+ "optional": true
92
+ },
80
93
  "@typescript/native-preview": {
81
94
  "optional": false
82
95
  },
96
+ "react": {
97
+ "optional": true
98
+ },
99
+ "react-dom": {
100
+ "optional": true
101
+ },
83
102
  "typescript": {
84
103
  "optional": false
85
104
  }
86
105
  },
87
- "engines": {
88
- "node": ">=24.0.0"
89
- },
90
106
  "files": [
91
107
  "!rslib-builder.api.json",
92
108
  "!tsconfig.json",
@@ -98,6 +114,8 @@
98
114
  "package.json",
99
115
  "tsconfig/ecma/lib.json",
100
116
  "tsconfig/root.json",
117
+ "tsconfig/rspress/plugin.json",
118
+ "tsconfig/rspress/website.json",
101
119
  "tsdoc-metadata.json"
102
120
  ]
103
121
  }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "declaration": true,
6
+ "exactOptionalPropertyTypes": true,
7
+ "jsx": "react-jsx",
8
+ "module": "esnext",
9
+ "moduleResolution": "bundler",
10
+ "resolveJsonModule": true,
11
+ "skipLibCheck": true,
12
+ "strict": true,
13
+ "strictNullChecks": true,
14
+ "target": "es2023"
15
+ },
16
+ "exclude": ["${configDir}/node_modules", "${configDir}/dist", "${configDir}/.rslib"],
17
+ "include": [
18
+ "${configDir}/src/**/*.ts",
19
+ "${configDir}/src/**/*.tsx",
20
+ "${configDir}/src/**/*.mts",
21
+ "${configDir}/src/**/*.cts",
22
+ "${configDir}/src/**/*.mdx",
23
+ "${configDir}/types/**/*.ts"
24
+ ]
25
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig.json",
3
+ "compilerOptions": {
4
+ "exactOptionalPropertyTypes": true,
5
+ "jsx": "react-jsx",
6
+ "lib": ["DOM", "ES2023"],
7
+ "module": "esnext",
8
+ "moduleResolution": "bundler",
9
+ "resolveJsonModule": true,
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "strictNullChecks": true,
13
+ "target": "es2023"
14
+ },
15
+ "exclude": ["${configDir}/node_modules", "${configDir}/dist", "${configDir}/.rspress", "${configDir}/.rslib"],
16
+ "include": [
17
+ "${configDir}/*.ts",
18
+ "${configDir}/*.mts",
19
+ "${configDir}/*.cts",
20
+ "${configDir}/**/*.tsx",
21
+ "${configDir}/docs/**/*.mdx"
22
+ ]
23
+ }