@savvy-web/rslib-builder 0.8.1 → 0.10.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
@@ -22,6 +22,10 @@ focus on your code.
22
22
  cleaner public APIs, with multi-entry support for packages with multiple exports
23
23
  - **Multi-Target Builds** - Separate dev (source maps) and npm (optimized)
24
24
  outputs
25
+ - **Virtual Entries** - Bundle additional files (like pnpmfile.cjs) with custom
26
+ output names, bypassing type generation and package.json exports
27
+ - **Flexible Format** - Configure output format (ESM or CJS) at the library level
28
+ with per-entry overrides for virtual entries
25
29
  - **PNPM Integration** - Automatically resolves `catalog:` and `workspace:`
26
30
  references
27
31
  - **Package.json Transform** - Converts `.ts` exports to `.js`, generates files
package/index.d.ts CHANGED
@@ -56,9 +56,12 @@ import ts from 'typescript';
56
56
 
57
57
  /**
58
58
  * Options for API model generation.
59
- * When enabled, generates an `<unscopedPackageName>.api.json` file using API Extractor.
59
+ * Generates an `<unscopedPackageName>.api.json` file using API Extractor.
60
60
  *
61
61
  * @remarks
62
+ * API model generation is enabled by default. To disable, set `apiModel: false`.
63
+ * Providing an options object implicitly enables API model generation.
64
+ *
62
65
  * API models are only generated for the main "index" entry point (the "." export).
63
66
  * Additional entry points like "./hooks" or "./utils" do not generate separate API models.
64
67
  * This prevents multiple conflicting API models and ensures a single source of truth
@@ -67,11 +70,6 @@ import ts from 'typescript';
67
70
  * @public
68
71
  */
69
72
  export declare interface ApiModelOptions {
70
- /**
71
- * Whether to enable API model generation.
72
- * @defaultValue false
73
- */
74
- enabled?: boolean;
75
73
  /**
76
74
  * Filename for the generated API model file.
77
75
  * @defaultValue `<unscopedPackageName>.api.json` (e.g., `rslib-builder.api.json`)
@@ -94,7 +92,6 @@ export declare interface ApiModelOptions {
94
92
  * import type { ApiModelOptions } from '@savvy-web/rslib-builder';
95
93
  *
96
94
  * const apiModel: ApiModelOptions = {
97
- * enabled: true,
98
95
  * localPaths: ['../docs-site/lib/packages/my-package'],
99
96
  * };
100
97
  * ```
@@ -114,7 +111,6 @@ export declare interface ApiModelOptions {
114
111
  * import type { ApiModelOptions } from '@savvy-web/rslib-builder';
115
112
  *
116
113
  * const apiModel: ApiModelOptions = {
117
- * enabled: true,
118
114
  * tsdoc: {
119
115
  * tagDefinitions: [{ tagName: '@error', syntaxKind: 'inline' }],
120
116
  * },
@@ -132,11 +128,12 @@ export declare interface ApiModelOptions {
132
128
  * A forgotten export occurs when a public API references a declaration
133
129
  * that isn't exported from the entry point.
134
130
  *
135
- * - `"include"` (default): Log a warning, include in the API model
131
+ * - `"include"`: Log a warning, include in the API model
136
132
  * - `"error"`: Fail the build with details about the forgotten exports
137
133
  * - `"ignore"`: Turn off detection — suppress all messages
138
134
  *
139
- * @defaultValue "include"
135
+ * @defaultValue `"error"` in CI environments (`CI` or `GITHUB_ACTIONS` env vars),
136
+ * `"include"` otherwise
140
137
  */
141
138
  forgottenExports?: "include" | "error" | "ignore";
142
139
  }
@@ -398,6 +395,13 @@ export declare interface DtsPluginOptions {
398
395
  * Used to generate the correct temp tsconfig when tsconfigPath is not provided.
399
396
  */
400
397
  buildTarget?: "dev" | "npm";
398
+ /**
399
+ * Output format for the library.
400
+ * Affects the resolved tsconfig.json module settings:
401
+ * - `"esm"`: Uses ESNext module and Bundler resolution (default)
402
+ * - `"cjs"`: Uses CommonJS module and Node10 resolution
403
+ */
404
+ format?: "esm" | "cjs";
401
405
  /**
402
406
  * Options for API model generation.
403
407
  * When enabled, generates an `<unscopedPackageName>.api.json` file in the dist directory.
@@ -1058,6 +1062,17 @@ export declare type JsonPrimitive = string | number | boolean | null;
1058
1062
  */
1059
1063
  export declare type JsonValue = JsonPrimitive | JsonObject | JsonArray;
1060
1064
 
1065
+ /**
1066
+ * Output format for library builds.
1067
+ *
1068
+ * @remarks
1069
+ * - `"esm"` - ES Modules format (sets `type: "module"` in package.json)
1070
+ * - `"cjs"` - CommonJS format (sets `type: "commonjs"` in package.json)
1071
+ *
1072
+ * @public
1073
+ */
1074
+ export declare type LibraryFormat = "esm" | "cjs";
1075
+
1061
1076
  /**
1062
1077
  * Allows creating a union type by combining primitive types and literal types
1063
1078
  * without sacrificing auto-completion in IDEs for the literal type part of the union.
@@ -1108,7 +1123,6 @@ export declare type LiteralUnion<LiteralType, BaseType extends Primitive> = Lite
1108
1123
  * export default NodeLibraryBuilder.create({
1109
1124
  * externals: ['@rslib/core', '@rsbuild/core'],
1110
1125
  * dtsBundledPackages: ['picocolors'],
1111
- * apiModel: true,
1112
1126
  * transform({ target, pkg }) {
1113
1127
  * if (target === 'npm') {
1114
1128
  * delete pkg.devDependencies;
@@ -1140,7 +1154,15 @@ export declare class NodeLibraryBuilder {
1140
1154
  * These defaults are merged with user-provided options in {@link NodeLibraryBuilder.mergeOptions}.
1141
1155
  * Arrays are deep-copied to prevent mutation of this object.
1142
1156
  */
1143
- static readonly DEFAULT_OPTIONS: NodeLibraryBuilderOptions;
1157
+ static readonly DEFAULT_OPTIONS: {
1158
+ format: "esm";
1159
+ plugins: never[];
1160
+ define: {};
1161
+ copyPatterns: never[];
1162
+ targets: ("dev" | "npm")[];
1163
+ externals: never[];
1164
+ apiModel: true;
1165
+ };
1144
1166
  /**
1145
1167
  * Merges user-provided options with default options.
1146
1168
  *
@@ -1176,12 +1198,65 @@ export declare class NodeLibraryBuilder {
1176
1198
  * @returns Promise resolving to the RSLib configuration
1177
1199
  */
1178
1200
  static createSingleTarget(target: BuildTarget, opts: NodeLibraryBuilderOptions): Promise<RslibConfig>;
1201
+ /**
1202
+ * Checks if the current package has exports defined in package.json.
1203
+ * @internal
1204
+ */
1205
+ private static packageHasExports;
1179
1206
  }
1180
1207
 
1181
1208
  /**
1182
1209
  * @public
1183
1210
  */
1184
1211
  export declare interface NodeLibraryBuilderOptions {
1212
+ /**
1213
+ * Output format for main entry points.
1214
+ * Also determines package.json `type` field:
1215
+ * - `"esm"` → `"type": "module"`
1216
+ * - `"cjs"` → `"type": "commonjs"`
1217
+ *
1218
+ * @defaultValue `"esm"`
1219
+ */
1220
+ format?: LibraryFormat;
1221
+ /**
1222
+ * Additional entry points bundled with custom output names.
1223
+ * These entries bypass type generation and package.json exports
1224
+ * but are included in the published package.
1225
+ *
1226
+ * @remarks
1227
+ * Virtual entries are useful for special files like pnpm config files
1228
+ * that need to be bundled but don't require type declarations or
1229
+ * exposure as package exports.
1230
+ *
1231
+ * A module may have ONLY virtualEntries with no regular entry points.
1232
+ *
1233
+ * @example
1234
+ * Mixed: regular entries + virtual entries
1235
+ * ```typescript
1236
+ * NodeLibraryBuilder.create({
1237
+ * virtualEntries: {
1238
+ * "pnpmfile.cjs": {
1239
+ * source: "./src/pnpmfile.ts",
1240
+ * format: "cjs",
1241
+ * },
1242
+ * },
1243
+ * })
1244
+ * ```
1245
+ *
1246
+ * @example
1247
+ * Virtual-only: no regular entry points
1248
+ * ```typescript
1249
+ * NodeLibraryBuilder.create({
1250
+ * format: "cjs",
1251
+ * virtualEntries: {
1252
+ * "pnpmfile.cjs": {
1253
+ * source: "./src/pnpmfile.ts",
1254
+ * },
1255
+ * },
1256
+ * })
1257
+ * ```
1258
+ */
1259
+ virtualEntries?: Record<string, VirtualEntryConfig>;
1185
1260
  /** Override entry points (optional - will auto-detect from package.json) */
1186
1261
  entry?: Record<string, string | string[]>;
1187
1262
  /**
@@ -1316,74 +1391,39 @@ export declare interface NodeLibraryBuilderOptions {
1316
1391
  transform?: TransformPackageJsonFn;
1317
1392
  /**
1318
1393
  * Options for API model generation.
1319
- * When enabled, generates an `<unscopedPackageName>.api.json` file in the dist directory.
1394
+ * Generates an `<unscopedPackageName>.api.json` file in the dist directory.
1320
1395
  * Only applies when target is "npm".
1321
1396
  *
1322
1397
  * @remarks
1323
- * The generated API model file contains the full API documentation
1324
- * in a machine-readable format for use by documentation generators.
1398
+ * API model generation is **enabled by default**. The generated file contains
1399
+ * full API documentation in a machine-readable format for documentation generators.
1325
1400
  * The file is emitted to dist but excluded from npm publish (added as negated pattern in `files` array).
1326
1401
  *
1402
+ * @defaultValue true
1403
+ *
1327
1404
  * @example
1328
- * Enable API model generation with defaults:
1405
+ * Disable API model generation:
1329
1406
  * ```typescript
1330
1407
  * import { NodeLibraryBuilder } from '@savvy-web/rslib-builder';
1331
1408
  *
1332
1409
  * export default NodeLibraryBuilder.create({
1333
- * apiModel: true,
1410
+ * apiModel: false,
1334
1411
  * });
1335
1412
  * ```
1336
1413
  *
1337
1414
  * @example
1338
- * Enable with custom filename:
1415
+ * Customize with options (implicitly enabled):
1339
1416
  * ```typescript
1340
1417
  * import { NodeLibraryBuilder } from '@savvy-web/rslib-builder';
1341
1418
  *
1342
1419
  * export default NodeLibraryBuilder.create({
1343
1420
  * apiModel: {
1344
- * enabled: true,
1345
1421
  * filename: 'my-package.api.json',
1346
1422
  * },
1347
1423
  * });
1348
1424
  * ```
1349
1425
  */
1350
1426
  apiModel?: ApiModelOptions | boolean;
1351
- /**
1352
- * Options for TSDoc lint validation.
1353
- * When enabled, validates TSDoc comments before the build starts.
1354
- *
1355
- * @remarks
1356
- * Uses ESLint with `eslint-plugin-tsdoc` to validate TSDoc syntax.
1357
- * By default, throws errors in CI environments and logs errors locally.
1358
- * The generated `tsdoc.json` config is persisted locally for IDE integration.
1359
- *
1360
- * @example
1361
- * Enable with defaults (throws in CI, errors locally):
1362
- * ```typescript
1363
- * import { NodeLibraryBuilder } from '@savvy-web/rslib-builder';
1364
- *
1365
- * export default NodeLibraryBuilder.create({
1366
- * tsdocLint: true,
1367
- * });
1368
- * ```
1369
- *
1370
- * @example
1371
- * Enable with custom configuration:
1372
- * ```typescript
1373
- * import { NodeLibraryBuilder } from '@savvy-web/rslib-builder';
1374
- *
1375
- * export default NodeLibraryBuilder.create({
1376
- * tsdocLint: {
1377
- * tsdoc: {
1378
- * tagDefinitions: [{ tagName: '@error', syntaxKind: 'block' }],
1379
- * },
1380
- * onError: 'throw',
1381
- * persistConfig: true,
1382
- * },
1383
- * });
1384
- * ```
1385
- */
1386
- tsdocLint?: TsDocLintPluginOptions | boolean;
1387
1427
  }
1388
1428
 
1389
1429
  /**
@@ -1880,6 +1920,15 @@ export declare const PackageJsonTransformPlugin: (options?: PackageJsonTransform
1880
1920
  * @public
1881
1921
  */
1882
1922
  export declare interface PackageJsonTransformPluginOptions {
1923
+ /**
1924
+ * Output format for the library.
1925
+ * Determines the package.json `type` field:
1926
+ * - `"esm"` → `"type": "module"`
1927
+ * - `"cjs"` → `"type": "commonjs"`
1928
+ *
1929
+ * @defaultValue `"esm"`
1930
+ */
1931
+ format?: LibraryFormat;
1883
1932
  /**
1884
1933
  * Override the package name in the output package.json.
1885
1934
  *
@@ -2364,8 +2413,14 @@ export declare class TsconfigResolver {
2364
2413
  * - Excludes file selection (include, exclude, files, references)
2365
2414
  * - Adds $schema for IDE support
2366
2415
  *
2416
+ * When `format` is `"cjs"`, the resolved config uses CommonJS-compatible settings:
2417
+ * - `module: "commonjs"`
2418
+ * - `moduleResolution: "node10"`
2419
+ * - `esModuleInterop: true`
2420
+ *
2367
2421
  * @param parsed - The parsed TypeScript configuration from `parseJsonConfigFileContent`
2368
2422
  * @param _rootDir - Reserved for future path normalization (currently unused)
2423
+ * @param format - Optional output format to adjust module settings (`"esm"` or `"cjs"`)
2369
2424
  * @returns A JSON-serializable tsconfig object
2370
2425
  * @throws {@link TsconfigResolverError} If resolution fails for any option
2371
2426
  *
@@ -2385,7 +2440,7 @@ export declare class TsconfigResolver {
2385
2440
  *
2386
2441
  * @public
2387
2442
  */
2388
- resolve(parsed: ParsedCommandLine, _rootDir: string): ResolvedTsconfig;
2443
+ resolve(parsed: ParsedCommandLine, _rootDir: string, format?: LibraryFormat): ResolvedTsconfig;
2389
2444
  /**
2390
2445
  * Adds converted enum options to the compiler options object.
2391
2446
  * @internal
@@ -2567,9 +2622,14 @@ export declare class TsconfigResolver {
2567
2622
  *
2568
2623
  * In CI environments (`CI` or `GITHUB_ACTIONS` env vars set to "true"),
2569
2624
  * this method validates the existing file instead of writing, throwing
2570
- * an error if the config is out of date.
2625
+ * an error if the config is out of date. Set `skipCIValidation` to true
2626
+ * to skip this validation (useful when `persistConfig: false`).
2627
+ *
2628
+ * @param options - TSDoc configuration options
2629
+ * @param outputDir - Directory to write the config file
2630
+ * @param skipCIValidation - Skip CI validation even in CI environments
2571
2631
  */
2572
- static writeConfigFile(options: TsDocOptions | undefined, outputDir: string): Promise<string>;
2632
+ static writeConfigFile(options: TsDocOptions | undefined, outputDir: string, skipCIValidation?: boolean): Promise<string>;
2573
2633
  /** Converts TSDocTagSyntaxKind enum to string format. */
2574
2634
  private static syntaxKindToString;
2575
2635
  }
@@ -2586,6 +2646,54 @@ export declare class TsconfigResolver {
2586
2646
  */
2587
2647
  export declare type TsDocLintErrorBehavior = "warn" | "error" | "throw";
2588
2648
 
2649
+ /**
2650
+ * Options for TSDoc lint validation (subset of TsDocLintPluginOptions).
2651
+ * TSDoc configuration is inherited from the parent TsDocOptions.
2652
+ *
2653
+ * @public
2654
+ */
2655
+ export declare interface TsDocLintOptions {
2656
+ /**
2657
+ * Override automatic file discovery with explicit file paths or glob patterns.
2658
+ *
2659
+ * @remarks
2660
+ * By default, uses import graph analysis to discover files from your package's exports.
2661
+ * This ensures only public API files are linted.
2662
+ *
2663
+ * @example
2664
+ * ```typescript
2665
+ * lint: {
2666
+ * include: ["src/**\/*.ts", "!**\/*.test.ts"],
2667
+ * }
2668
+ * ```
2669
+ */
2670
+ include?: string[];
2671
+ /**
2672
+ * How to handle TSDoc lint errors.
2673
+ * - `"warn"`: Log warnings but continue the build
2674
+ * - `"error"`: Log errors but continue the build
2675
+ * - `"throw"`: Fail the build with an error
2676
+ *
2677
+ * @defaultValue `"throw"` in CI environments, `"error"` locally
2678
+ */
2679
+ onError?: TsDocLintErrorBehavior;
2680
+ /**
2681
+ * Persist tsdoc.json to disk for tool integration (ESLint, IDEs).
2682
+ * - `true`: Write to project root as "tsdoc.json" (or validate in CI)
2683
+ * - `PathLike`: Write to specified path (or validate in CI)
2684
+ * - `false`: Clean up after linting (not recommended)
2685
+ *
2686
+ * @remarks
2687
+ * In CI environments (`CI` or `GITHUB_ACTIONS` env vars set to "true"),
2688
+ * the config file is validated instead of written. If the existing file
2689
+ * doesn't match the expected configuration, the build fails with an error
2690
+ * instructing the developer to regenerate the file locally.
2691
+ *
2692
+ * @defaultValue `true`
2693
+ */
2694
+ persistConfig?: boolean | PathLike;
2695
+ }
2696
+
2589
2697
  /**
2590
2698
  * Creates a plugin to validate TSDoc comments before build using ESLint with eslint-plugin-tsdoc.
2591
2699
  *
@@ -2815,21 +2923,6 @@ export declare class TsconfigResolver {
2815
2923
  * ```
2816
2924
  */
2817
2925
  supportForTags?: Record<string, boolean>;
2818
- /**
2819
- * Persist tsdoc.json to disk for tool integration (ESLint, IDEs).
2820
- * - `true`: Write to project root as "tsdoc.json" (or validate in CI)
2821
- * - `PathLike`: Write to specified path (or validate in CI)
2822
- * - `false`: Clean up after API Extractor (not recommended)
2823
- *
2824
- * @remarks
2825
- * In CI environments (`CI` or `GITHUB_ACTIONS` env vars set to "true"),
2826
- * the config file is validated instead of written. If the existing file
2827
- * doesn't match the expected configuration, the build fails with an error
2828
- * instructing the developer to regenerate the file locally.
2829
- *
2830
- * @defaultValue `true`
2831
- */
2832
- persistConfig?: boolean | PathLike;
2833
2926
  /**
2834
2927
  * How to handle TSDoc validation warnings from API Extractor.
2835
2928
  * - `"log"`: Show warnings in the console but continue the build
@@ -2849,6 +2942,42 @@ export declare class TsconfigResolver {
2849
2942
  * `"log"` otherwise
2850
2943
  */
2851
2944
  warnings?: "log" | "fail" | "none";
2945
+ /**
2946
+ * TSDoc lint validation options.
2947
+ * Validates TSDoc comments before the build starts using ESLint.
2948
+ *
2949
+ * @remarks
2950
+ * Lint validation is **enabled by default**. Set to `false` to disable.
2951
+ * When enabled, it uses the parent TSDoc configuration (tagDefinitions, groups, etc.)
2952
+ * for validation rules.
2953
+ *
2954
+ * @defaultValue true
2955
+ *
2956
+ * @example
2957
+ * Disable lint validation:
2958
+ * ```typescript
2959
+ * apiModel: {
2960
+ * tsdoc: {
2961
+ * lint: false,
2962
+ * },
2963
+ * }
2964
+ * ```
2965
+ *
2966
+ * @example
2967
+ * Customize lint behavior:
2968
+ * ```typescript
2969
+ * apiModel: {
2970
+ * tsdoc: {
2971
+ * tagDefinitions: [{ tagName: '@error', syntaxKind: 'inline' }],
2972
+ * lint: {
2973
+ * onError: 'throw',
2974
+ * include: ['src/**\/*.ts'],
2975
+ * },
2976
+ * },
2977
+ * }
2978
+ * ```
2979
+ */
2980
+ lint?: TsDocLintOptions | boolean;
2852
2981
  }
2853
2982
 
2854
2983
  /**
@@ -2891,4 +3020,72 @@ export declare class TsconfigResolver {
2891
3020
  */
2892
3021
  export declare type TsDocTagGroup = "core" | "extended" | "discretionary";
2893
3022
 
3023
+ /**
3024
+ * Configuration for a virtual entry point.
3025
+ *
3026
+ * @remarks
3027
+ * Virtual entries are bundled entry points that bypass type generation
3028
+ * and package.json exports while still being included in the published package.
3029
+ *
3030
+ * @example
3031
+ * ```typescript
3032
+ * const config: VirtualEntryConfig = {
3033
+ * source: "./src/pnpmfile.ts",
3034
+ * format: "cjs",
3035
+ * };
3036
+ * ```
3037
+ *
3038
+ * @public
3039
+ */
3040
+ export declare interface VirtualEntryConfig {
3041
+ /**
3042
+ * Path to source file (relative to package root).
3043
+ */
3044
+ source: string;
3045
+ /**
3046
+ * Output format for this entry.
3047
+ * If not specified, inherits from top-level `format` option.
3048
+ */
3049
+ format?: LibraryFormat;
3050
+ }
3051
+
3052
+ /**
3053
+ * Plugin to handle virtual entry points in RSlib builds.
3054
+ *
3055
+ * @remarks
3056
+ * Virtual entries are special entry points that:
3057
+ * - Are bundled like regular entries
3058
+ * - Do NOT generate TypeScript declarations (.d.ts files)
3059
+ * - Are NOT added to package.json exports
3060
+ * - ARE added to package.json files array for publishing
3061
+ *
3062
+ * This plugin:
3063
+ * 1. Exposes the virtual entry names for DtsPlugin to skip type generation
3064
+ * 2. Adds virtual entry outputs to the files array in the additional stage
3065
+ *
3066
+ * @example
3067
+ * ```typescript
3068
+ * import { VirtualEntryPlugin } from '@savvy-web/rslib-builder';
3069
+ *
3070
+ * const plugin = VirtualEntryPlugin({
3071
+ * virtualEntryNames: new Set(['pnpmfile']),
3072
+ * });
3073
+ * ```
3074
+ *
3075
+ * @public
3076
+ */
3077
+ export declare const VirtualEntryPlugin: (options: VirtualEntryPluginOptions) => RsbuildPlugin;
3078
+
3079
+ /**
3080
+ * Options for the VirtualEntryPlugin.
3081
+ * @public
3082
+ */
3083
+ export declare interface VirtualEntryPluginOptions {
3084
+ /**
3085
+ * Set of virtual entry names (without extensions).
3086
+ * Used by DtsPlugin to skip type generation for these entries.
3087
+ */
3088
+ virtualEntryNames: Set<string>;
3089
+ }
3090
+
2894
3091
  export { }
package/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /*! For license information please see index.js.LICENSE.txt */
2
- import { constants, existsSync, writeFileSync } from "node:fs";
2
+ import { constants, existsSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { dirname, isAbsolute, join, normalize, relative, resolve as external_node_path_resolve } from "node:path";
4
4
  import { defineConfig } from "@rslib/core";
5
5
  import { access, copyFile, mkdir, readFile, readdir, rm, stat, unlink as promises_unlink, writeFile } from "node:fs/promises";
6
6
  import { logger as core_logger } from "@rsbuild/core";
7
7
  import picocolors from "picocolors";
8
- import { getWorkspaceRoot } from "workspace-tools";
8
+ import { getWorkspaceManagerRoot } from "workspace-tools";
9
9
  import { spawn } from "node:child_process";
10
10
  import { StandardTags, Standardization, TSDocTagSyntaxKind } from "@microsoft/tsdoc";
11
11
  import deep_equal from "deep-equal";
@@ -169,7 +169,7 @@ function getApiExtractorPath() {
169
169
  const cwd = process.cwd();
170
170
  const localApiExtractor = join(cwd, "node_modules", "@microsoft", "api-extractor");
171
171
  if (existsSync(localApiExtractor)) return localApiExtractor;
172
- const workspaceRoot = getWorkspaceRoot(cwd);
172
+ const workspaceRoot = getWorkspaceManagerRoot(cwd);
173
173
  if (workspaceRoot) {
174
174
  const workspaceApiExtractor = join(workspaceRoot, "node_modules", "@microsoft", "api-extractor");
175
175
  if (existsSync(workspaceApiExtractor)) return workspaceApiExtractor;
@@ -205,9 +205,9 @@ const AutoEntryPlugin = (options)=>{
205
205
  try {
206
206
  const packageJsonContent = await readFile(assetPath, "utf-8");
207
207
  const packageJson = JSON.parse(packageJsonContent);
208
- const { entries } = extractEntriesFromPackageJson(packageJson, {
209
- exportsAsIndexes: options?.exportsAsIndexes
210
- });
208
+ const { entries } = extractEntriesFromPackageJson(packageJson, options?.exportsAsIndexes != null ? {
209
+ exportsAsIndexes: options.exportsAsIndexes
210
+ } : void 0);
211
211
  if (options?.exportsAsIndexes && packageJson.exports) {
212
212
  const exports = packageJson.exports;
213
213
  if ("object" == typeof exports && !Array.isArray(exports)) for (const pkgExportKey of Object.keys(exports)){
@@ -255,7 +255,7 @@ const AutoEntryPlugin = (options)=>{
255
255
  }
256
256
  };
257
257
  };
258
- 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,"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}/public/**/*.json"]}');
258
+ 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}/public/**/*.json"]}');
259
259
  const requireCJS = createRequire(import.meta.url);
260
260
  const jsonImports = new Map([
261
261
  [
@@ -320,28 +320,43 @@ class LibraryTSConfigFile extends TSConfigFile {
320
320
  outDir: "dist",
321
321
  tsBuildInfoFile: `${process.cwd()}/dist/.tsbuildinfo.${target}.bundle`
322
322
  },
323
- include
323
+ ...void 0 !== include && include.length > 0 ? {
324
+ include
325
+ } : {}
324
326
  };
325
327
  }
326
- writeBundleTempConfig(target) {
328
+ writeBundleTempConfig(_target) {
327
329
  const cwd = process.cwd();
328
- const config = this.bundle(target);
329
- const toAbsolute = (path)=>{
330
- if (path.startsWith("../") || ".." === path) return path.replace(/^((\.\.\/)+\.\.?|\.\.\/*)$/, cwd).replace(/^(\.\.\/)+/, `${cwd}/`);
331
- return path;
332
- };
330
+ const baseConfig = this.config;
333
331
  const absoluteConfig = {
334
- ...config,
332
+ $schema: "https://json.schemastore.org/tsconfig.json",
335
333
  compilerOptions: {
336
- ...config.compilerOptions,
334
+ ...baseConfig.compilerOptions,
335
+ composite: false,
336
+ incremental: false,
337
+ tsBuildInfoFile: void 0,
337
338
  rootDir: cwd,
339
+ outDir: join(cwd, "dist"),
340
+ declarationDir: join(cwd, "dist"),
341
+ typeRoots: [
342
+ join(cwd, "node_modules/@types"),
343
+ join(cwd, "types")
344
+ ],
338
345
  declarationMap: true,
339
- emitDeclarationOnly: false,
340
- declarationDir: config.compilerOptions?.declarationDir ? toAbsolute(config.compilerOptions.declarationDir) : void 0,
341
- typeRoots: config.compilerOptions?.typeRoots?.map(toAbsolute)
346
+ emitDeclarationOnly: false
342
347
  },
343
- include: config.include?.map(toAbsolute),
344
- exclude: config.exclude?.map(toAbsolute)
348
+ include: [
349
+ join(cwd, "src/**/*.ts"),
350
+ join(cwd, "src/**/*.mts"),
351
+ join(cwd, "src/**/*.tsx"),
352
+ join(cwd, "src/**/*.json"),
353
+ join(cwd, "types/*.ts"),
354
+ join(cwd, "package.json")
355
+ ],
356
+ exclude: [
357
+ join(cwd, "node_modules"),
358
+ join(cwd, "dist/**/*")
359
+ ]
345
360
  };
346
361
  const tmpFile = requireCJS("tmp").fileSync({
347
362
  prefix: "tsconfig-bundle-",
@@ -365,8 +380,8 @@ class TsconfigResolverError extends Error {
365
380
  constructor(message, option, value){
366
381
  super(message);
367
382
  this.name = "TsconfigResolverError";
368
- this.option = option;
369
- this.value = value;
383
+ if (void 0 !== option) this.option = option;
384
+ if (void 0 !== value) this.value = value;
370
385
  }
371
386
  }
372
387
  const TSCONFIG_SCHEMA_URL = "https://json.schemastore.org/tsconfig";
@@ -664,7 +679,7 @@ class TsconfigResolver {
664
679
  const filename = lib.includes("/") || lib.includes("\\") ? lib.split(/[\\/]/).pop() ?? lib : lib;
665
680
  return filename.replace(/^lib\./, "").replace(/\.d\.ts$/, "");
666
681
  }
667
- resolve(parsed, _rootDir) {
682
+ resolve(parsed, _rootDir, format) {
668
683
  const opts = parsed.options;
669
684
  const compilerOptions = {};
670
685
  this.addEnumOptions(compilerOptions, opts);
@@ -673,6 +688,11 @@ class TsconfigResolver {
673
688
  compilerOptions.noEmit = true;
674
689
  this.addPreservedBooleanOptions(compilerOptions, opts);
675
690
  this.addPreservedStringOptions(compilerOptions, opts);
691
+ if ("cjs" === format) {
692
+ compilerOptions.module = "commonjs";
693
+ compilerOptions.moduleResolution = "node10";
694
+ compilerOptions.esModuleInterop = true;
695
+ }
676
696
  return {
677
697
  $schema: TSCONFIG_SCHEMA_URL,
678
698
  compilerOptions
@@ -786,9 +806,9 @@ class TsDocConfigBuilder {
786
806
  strict: true
787
807
  })) throw new Error(`tsdoc.json is out of date. Run the build locally to regenerate it, then commit the changes.\nExpected:\n${JSON.stringify(expectedConfig, null, 2)}\n\nActual:\n${JSON.stringify(existingConfig, null, 2)}`);
788
808
  }
789
- static async writeConfigFile(options = {}, outputDir) {
809
+ static async writeConfigFile(options = {}, outputDir, skipCIValidation = false) {
790
810
  const configPath = join(outputDir, "tsdoc.json");
791
- if (TsDocConfigBuilder.isCI()) {
811
+ if (TsDocConfigBuilder.isCI() && !skipCIValidation) {
792
812
  await TsDocConfigBuilder.validateConfigFile(options, configPath);
793
813
  return configPath;
794
814
  }
@@ -821,7 +841,7 @@ function getTsgoBinPath() {
821
841
  const cwd = process.cwd();
822
842
  const localTsgoBin = join(cwd, "node_modules", ".bin", "tsgo");
823
843
  if (existsSync(localTsgoBin)) return localTsgoBin;
824
- const workspaceRoot = getWorkspaceRoot(cwd);
844
+ const workspaceRoot = getWorkspaceManagerRoot(cwd);
825
845
  if (workspaceRoot) {
826
846
  const workspaceTsgoBin = join(workspaceRoot, "node_modules", ".bin", "tsgo");
827
847
  if (existsSync(workspaceTsgoBin)) return workspaceTsgoBin;
@@ -869,25 +889,27 @@ async function bundleDtsFiles(options) {
869
889
  const bundledFiles = new Map();
870
890
  let apiModelPath;
871
891
  let tsdocMetadataPath;
872
- const apiModelEnabled = true === apiModel || "object" == typeof apiModel && (void 0 === apiModel.enabled || true === apiModel.enabled);
892
+ const apiModelEnabled = true === apiModel || "object" == typeof apiModel;
873
893
  const apiModelFilename = "object" == typeof apiModel && apiModel.filename ? apiModel.filename : "api.json";
874
894
  const tsdocOptions = "object" == typeof apiModel ? apiModel.tsdoc : void 0;
875
895
  const tsdocMetadataOption = "object" == typeof apiModel ? apiModel.tsdocMetadata : void 0;
876
896
  const tsdocWarnings = tsdocOptions?.warnings ?? (TsDocConfigBuilder.isCI() ? "fail" : "log");
877
- const forgottenExports = ("object" == typeof apiModel ? apiModel.forgottenExports : void 0) ?? "include";
897
+ const forgottenExports = ("object" == typeof apiModel ? apiModel.forgottenExports : void 0) ?? (TsDocConfigBuilder.isCI() ? "error" : "include");
878
898
  const tsdocMetadataEnabled = apiModelEnabled && (void 0 === tsdocMetadataOption || true === tsdocMetadataOption || "object" == typeof tsdocMetadataOption && false !== tsdocMetadataOption.enabled);
879
899
  const tsdocMetadataFilename = "object" == typeof tsdocMetadataOption && tsdocMetadataOption.filename ? tsdocMetadataOption.filename : "tsdoc-metadata.json";
880
900
  getApiExtractorPath();
881
- const persistConfig = tsdocOptions?.persistConfig;
901
+ const lintConfig = "object" == typeof tsdocOptions?.lint ? tsdocOptions.lint : void 0;
902
+ const persistConfig = lintConfig?.persistConfig;
882
903
  const tsdocConfigOutputPath = TsDocConfigBuilder.getConfigPath(persistConfig, cwd);
904
+ const skipCIValidation = false === persistConfig;
883
905
  let tsdocConfigPath;
884
906
  let tsdocConfigFile;
885
907
  if (apiModelEnabled) {
886
- tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions ?? {}, dirname(tsdocConfigOutputPath));
908
+ tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions ?? {}, dirname(tsdocConfigOutputPath), skipCIValidation);
887
909
  const { TSDocConfigFile } = await import("@microsoft/tsdoc-config");
888
910
  tsdocConfigFile = TSDocConfigFile.loadForFolder(dirname(tsdocConfigPath));
889
911
  }
890
- const { Extractor, ExtractorConfig } = await import("@microsoft/api-extractor");
912
+ const { Extractor, ExtractorConfig, ExtractorMessage, ExtractorLogLevel } = await import("@microsoft/api-extractor");
891
913
  for (const [entryName, sourcePath] of entryPoints){
892
914
  const normalizedSourcePath = sourcePath.replace(/^\.\//, "");
893
915
  const dtsFileName = normalizedSourcePath.replace(/\.(tsx?|jsx?)$/, ".d.ts");
@@ -910,7 +932,7 @@ async function bundleDtsFiles(options) {
910
932
  const tempApiModelPath = generateApiModel ? join(tempOutputDir, apiModelFilename) : void 0;
911
933
  const generateTsdocMetadata = tsdocMetadataEnabled && "index" === entryName;
912
934
  const tempTsdocMetadataPath = generateTsdocMetadata ? join(tempOutputDir, tsdocMetadataFilename) : void 0;
913
- const extractorConfig = ExtractorConfig.prepare({
935
+ const prepareOptions = {
914
936
  configObject: {
915
937
  projectFolder: cwd,
916
938
  mainEntryPointFilePath: tempDtsPath,
@@ -922,20 +944,29 @@ async function bundleDtsFiles(options) {
922
944
  enabled: true,
923
945
  untrimmedFilePath: tempBundledPath
924
946
  },
925
- docModel: generateApiModel ? {
926
- enabled: true,
927
- apiJsonFilePath: tempApiModelPath
928
- } : void 0,
929
- tsdocMetadata: generateTsdocMetadata ? {
930
- enabled: true,
931
- tsdocMetadataFilePath: tempTsdocMetadataPath
932
- } : void 0,
947
+ ...generateApiModel && {
948
+ docModel: {
949
+ enabled: true,
950
+ ...tempApiModelPath && {
951
+ apiJsonFilePath: tempApiModelPath
952
+ }
953
+ }
954
+ },
955
+ ...generateTsdocMetadata && {
956
+ tsdocMetadata: {
957
+ enabled: true,
958
+ ...tempTsdocMetadataPath && {
959
+ tsdocMetadataFilePath: tempTsdocMetadataPath
960
+ }
961
+ }
962
+ },
933
963
  bundledPackages: bundledPackages
934
964
  },
935
965
  packageJsonFullPath: join(cwd, "package.json"),
936
- configObjectFullPath: void 0,
937
- tsdocConfigFile: tsdocConfigFile
938
- });
966
+ configObjectFullPath: void 0
967
+ };
968
+ if (tsdocConfigFile) prepareOptions.tsdocConfigFile = tsdocConfigFile;
969
+ const extractorConfig = ExtractorConfig.prepare(prepareOptions);
939
970
  const collectedTsdocWarnings = [];
940
971
  const collectedForgottenExports = [];
941
972
  const extractorResult = Extractor.invoke(extractorConfig, {
@@ -943,33 +974,45 @@ async function bundleDtsFiles(options) {
943
974
  showVerboseMessages: false,
944
975
  messageCallback: (message)=>{
945
976
  if (message.text?.includes("Analysis will use the bundled TypeScript version") || message.text?.includes("The target project appears to use TypeScript")) {
946
- message.logLevel = "none";
977
+ message.logLevel = ExtractorLogLevel.None;
947
978
  return;
948
979
  }
949
980
  if (message.text?.includes("You have changed the public API signature")) {
950
- message.logLevel = "none";
981
+ message.logLevel = ExtractorLogLevel.None;
951
982
  return;
952
983
  }
953
984
  const isTsdocMessage = message.messageId?.startsWith("tsdoc-");
954
- if (isTsdocMessage && message.text) if ("none" === tsdocWarnings) message.logLevel = "none";
985
+ if (isTsdocMessage && message.text) if ("none" === tsdocWarnings) message.logLevel = ExtractorLogLevel.None;
955
986
  else {
956
987
  collectedTsdocWarnings.push({
957
988
  text: message.text,
958
- sourceFilePath: message.sourceFilePath,
959
- sourceFileLine: message.sourceFileLine,
960
- sourceFileColumn: message.sourceFileColumn
989
+ ...null != message.sourceFilePath && {
990
+ sourceFilePath: message.sourceFilePath
991
+ },
992
+ ...null != message.sourceFileLine && {
993
+ sourceFileLine: message.sourceFileLine
994
+ },
995
+ ...null != message.sourceFileColumn && {
996
+ sourceFileColumn: message.sourceFileColumn
997
+ }
961
998
  });
962
- message.logLevel = "none";
999
+ message.logLevel = ExtractorLogLevel.None;
963
1000
  }
964
- if ("ae-forgotten-export" === message.messageId && message.text) if ("ignore" === forgottenExports) message.logLevel = "none";
1001
+ if ("ae-forgotten-export" === message.messageId && message.text) if ("ignore" === forgottenExports) message.logLevel = ExtractorLogLevel.None;
965
1002
  else {
966
1003
  collectedForgottenExports.push({
967
1004
  text: message.text,
968
- sourceFilePath: message.sourceFilePath,
969
- sourceFileLine: message.sourceFileLine,
970
- sourceFileColumn: message.sourceFileColumn
1005
+ ...null != message.sourceFilePath && {
1006
+ sourceFilePath: message.sourceFilePath
1007
+ },
1008
+ ...null != message.sourceFileLine && {
1009
+ sourceFileLine: message.sourceFileLine
1010
+ },
1011
+ ...null != message.sourceFileColumn && {
1012
+ sourceFileColumn: message.sourceFileColumn
1013
+ }
971
1014
  });
972
- message.logLevel = "none";
1015
+ message.logLevel = ExtractorLogLevel.None;
973
1016
  }
974
1017
  }
975
1018
  });
@@ -1009,9 +1052,15 @@ async function bundleDtsFiles(options) {
1009
1052
  }
1010
1053
  return {
1011
1054
  bundledFiles,
1012
- apiModelPath,
1013
- tsdocMetadataPath,
1014
- tsdocConfigPath
1055
+ ...apiModelPath && {
1056
+ apiModelPath
1057
+ },
1058
+ ...tsdocMetadataPath && {
1059
+ tsdocMetadataPath
1060
+ },
1061
+ ...tsdocConfigPath && {
1062
+ tsdocConfigPath
1063
+ }
1015
1064
  };
1016
1065
  }
1017
1066
  function stripSourceMapComment(content) {
@@ -1045,8 +1094,12 @@ function runTsgo(options) {
1045
1094
  const args = generateTsgoArgs({
1046
1095
  configPath,
1047
1096
  declarationDir,
1048
- rootDir,
1049
- tsBuildInfoFile
1097
+ ...rootDir && {
1098
+ rootDir
1099
+ },
1100
+ ...tsBuildInfoFile && {
1101
+ tsBuildInfoFile
1102
+ }
1050
1103
  });
1051
1104
  return new Promise((resolve)=>{
1052
1105
  const child = spawn(tsgoBinPath, args, {
@@ -1098,17 +1151,16 @@ function runTsgo(options) {
1098
1151
  const log = createEnvLogger("dts");
1099
1152
  const cwd = api.context.rootPath;
1100
1153
  try {
1101
- let configTsconfigPath = options.tsconfigPath || config.source?.tsconfigPath;
1102
- if (!configTsconfigPath && options.buildTarget) {
1154
+ let configTsconfigPath = options.tsconfigPath;
1155
+ if (options.buildTarget) {
1103
1156
  const originalCwd = process.cwd();
1104
1157
  try {
1105
1158
  process.chdir(cwd);
1106
1159
  configTsconfigPath = TSConfigs.node.ecma.lib.writeBundleTempConfig(options.buildTarget);
1107
- log.global.info(`Using tsconfig: ${configTsconfigPath}`);
1108
1160
  } finally{
1109
1161
  process.chdir(originalCwd);
1110
1162
  }
1111
- }
1163
+ } else if (!configTsconfigPath) configTsconfigPath = config.source?.tsconfigPath;
1112
1164
  state.tsconfigPath = findTsConfig(cwd, configTsconfigPath);
1113
1165
  if (!state.tsconfigPath) {
1114
1166
  const error = new Error(`Failed to resolve tsconfig file ${picocolors.cyan(`"${config.source?.tsconfigPath ?? "tsconfig.json"}"`)} from ${picocolors.cyan(cwd)}. Please ensure that the file exists.`);
@@ -1137,8 +1189,12 @@ function runTsgo(options) {
1137
1189
  const { success, output } = await runTsgo({
1138
1190
  configPath: state.tsconfigPath,
1139
1191
  declarationDir: tempDtsDir,
1140
- rootDir: state.parsedConfig.options.rootDir,
1141
- tsBuildInfoFile: state.parsedConfig.options.tsBuildInfoFile,
1192
+ ...state.parsedConfig.options.rootDir && {
1193
+ rootDir: state.parsedConfig.options.rootDir
1194
+ },
1195
+ ...state.parsedConfig.options.tsBuildInfoFile && {
1196
+ tsBuildInfoFile: state.parsedConfig.options.tsBuildInfoFile
1197
+ },
1142
1198
  name: envId
1143
1199
  });
1144
1200
  if (!success) {
@@ -1189,8 +1245,13 @@ function runTsgo(options) {
1189
1245
  const extractor = new EntryExtractor();
1190
1246
  const { entries } = extractor.extract(packageJson);
1191
1247
  const entryPoints = new Map();
1248
+ const virtualEntryNames = api.useExposed("virtual-entry-names");
1192
1249
  for (const [entryName, sourcePath] of Object.entries(entries)){
1193
1250
  if (entryName.startsWith("bin/")) continue;
1251
+ if (virtualEntryNames?.has(entryName)) {
1252
+ log.global.info(`Skipping type generation for virtual entry: ${entryName}`);
1253
+ continue;
1254
+ }
1194
1255
  if (!sourcePath.match(/\.(ts|mts|cts|tsx)$/)) continue;
1195
1256
  if (sourcePath.includes(".test.") || sourcePath.includes("__test__")) continue;
1196
1257
  const resolvedSourcePath = sourcePath.startsWith(".") ? join(cwd, sourcePath) : sourcePath;
@@ -1218,9 +1279,15 @@ function runTsgo(options) {
1218
1279
  tsconfigPath: state.tsconfigPath,
1219
1280
  bundledPackages: options.bundledPackages || [],
1220
1281
  entryPoints,
1221
- banner: options.banner,
1222
- footer: options.footer,
1223
- apiModel: options.apiModel
1282
+ ...options.banner && {
1283
+ banner: options.banner
1284
+ },
1285
+ ...options.footer && {
1286
+ footer: options.footer
1287
+ },
1288
+ ...void 0 !== options.apiModel && {
1289
+ apiModel: options.apiModel
1290
+ }
1224
1291
  });
1225
1292
  let emittedCount = 0;
1226
1293
  for (const [entryName, tempBundledPath] of bundledFiles){
@@ -1275,7 +1342,7 @@ function runTsgo(options) {
1275
1342
  }
1276
1343
  if (apiModelPath && state.parsedConfig && state.tsconfigPath) {
1277
1344
  const resolver = new TsconfigResolver();
1278
- const resolvedTsconfig = resolver.resolve(state.parsedConfig, cwd);
1345
+ const resolvedTsconfig = resolver.resolve(state.parsedConfig, cwd, options.format);
1279
1346
  const tsconfigContent = `${JSON.stringify(resolvedTsconfig, null, "\t")}\n`;
1280
1347
  const tsconfigSource = new context.sources.OriginalSource(tsconfigContent, "tsconfig.json");
1281
1348
  context.compilation.emitAsset("tsconfig.json", tsconfigSource);
@@ -1521,7 +1588,7 @@ class PnpmCatalog {
1521
1588
  async getCatalog() {
1522
1589
  try {
1523
1590
  if (!this.cachedWorkspaceRoot) {
1524
- this.cachedWorkspaceRoot = getWorkspaceRoot(process.cwd());
1591
+ this.cachedWorkspaceRoot = getWorkspaceManagerRoot(process.cwd());
1525
1592
  if (!this.cachedWorkspaceRoot) throw new Error("Could not find workspace root - ensure you're in a workspace");
1526
1593
  }
1527
1594
  const workspaceFile = external_node_path_resolve(this.cachedWorkspaceRoot, "pnpm-workspace.yaml");
@@ -1742,7 +1809,10 @@ function applyRslibTransformations(packageJson, originalPackageJson, processTSEx
1742
1809
  private: isPrivate
1743
1810
  };
1744
1811
  if (processedManifest.exports) processedManifest.exports = transformPackageExports(processedManifest.exports, processTSExports, void 0, entrypoints, exportToOutputMap, bundle ?? false);
1745
- if (processedManifest.bin) processedManifest.bin = transformPackageBin(processedManifest.bin, processTSExports);
1812
+ if (processedManifest.bin) {
1813
+ const transformedBin = transformPackageBin(processedManifest.bin, processTSExports);
1814
+ if (transformedBin) processedManifest.bin = transformedBin;
1815
+ }
1746
1816
  if (originalPackageJson.typesVersions) {
1747
1817
  const transformedTypesVersions = {};
1748
1818
  for (const [version, paths] of Object.entries(originalPackageJson.typesVersions)){
@@ -1804,6 +1874,7 @@ const PackageJsonTransformPlugin = (options = {})=>{
1804
1874
  const processedPackageJson = await buildPackageJson(packageJson.data, isProduction, options.processTSExports, entrypoints, exportToOutputMap, options.bundle, options.transform);
1805
1875
  packageJson.data = processedPackageJson;
1806
1876
  if (options.forcePrivate) packageJson.data.private = true;
1877
+ if (options.format) packageJson.data.type = "esm" === options.format ? "module" : "commonjs";
1807
1878
  const useRollupTypes = api.useExposed("use-rollup-types");
1808
1879
  if (useRollupTypes && packageJson.data.exports && "object" == typeof packageJson.data.exports) {
1809
1880
  const exports = packageJson.data.exports;
@@ -2091,7 +2162,8 @@ async function runTsDocLint(options, cwd) {
2091
2162
  const persistConfig = options.persistConfig;
2092
2163
  const shouldPersist = TsDocConfigBuilder.shouldPersist(persistConfig);
2093
2164
  const tsdocConfigOutputPath = TsDocConfigBuilder.getConfigPath(persistConfig, cwd);
2094
- const tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions, dirname(tsdocConfigOutputPath));
2165
+ const skipCIValidation = false === persistConfig;
2166
+ const tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions, dirname(tsdocConfigOutputPath), skipCIValidation);
2095
2167
  const eslintModule = await import("eslint");
2096
2168
  const tsParserModule = await import("@typescript-eslint/parser");
2097
2169
  const tsdocPluginModule = await import("eslint-plugin-tsdoc");
@@ -2105,8 +2177,12 @@ async function runTsDocLint(options, cwd) {
2105
2177
  warningCount: 0,
2106
2178
  messages: []
2107
2179
  },
2108
- tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
2109
- discoveryErrors: discovery.errors
2180
+ ...shouldPersist && {
2181
+ tsdocConfigPath
2182
+ },
2183
+ ...discovery.errors.length > 0 && {
2184
+ discoveryErrors: discovery.errors
2185
+ }
2110
2186
  };
2111
2187
  let eslintConfig;
2112
2188
  let filesToLint;
@@ -2189,8 +2265,12 @@ async function runTsDocLint(options, cwd) {
2189
2265
  warningCount,
2190
2266
  messages
2191
2267
  },
2192
- tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
2193
- discoveryErrors: discovery.errors.length > 0 ? discovery.errors : void 0
2268
+ ...shouldPersist && {
2269
+ tsdocConfigPath
2270
+ },
2271
+ ...discovery.errors.length > 0 && {
2272
+ discoveryErrors: discovery.errors
2273
+ }
2194
2274
  };
2195
2275
  }
2196
2276
  async function cleanupTsDocConfig(configPath) {
@@ -2233,13 +2313,35 @@ const TsDocLintPlugin = (options = {})=>{
2233
2313
  }
2234
2314
  };
2235
2315
  };
2316
+ const VirtualEntryPlugin = (options)=>{
2317
+ const { virtualEntryNames } = options;
2318
+ return {
2319
+ name: "virtual-entry-plugin",
2320
+ setup (api) {
2321
+ api.expose("virtual-entry-names", virtualEntryNames);
2322
+ api.processAssets({
2323
+ stage: "additional"
2324
+ }, async (context)=>{
2325
+ let filesArray = api.useExposed("files-array");
2326
+ if (!filesArray) {
2327
+ filesArray = new Set();
2328
+ api.expose("files-array", filesArray);
2329
+ }
2330
+ for (const assetName of Object.keys(context.compilation.assets)){
2331
+ const baseName = assetName.replace(/\.(c|m)?js$/, "");
2332
+ if (virtualEntryNames.has(baseName)) filesArray.add(assetName);
2333
+ }
2334
+ });
2335
+ }
2336
+ };
2337
+ };
2236
2338
  /* v8 ignore next -- @preserve */ class NodeLibraryBuilder {
2237
2339
  static VALID_TARGETS = [
2238
2340
  "dev",
2239
2341
  "npm"
2240
2342
  ];
2241
2343
  static DEFAULT_OPTIONS = {
2242
- entry: void 0,
2344
+ format: "esm",
2243
2345
  plugins: [],
2244
2346
  define: {},
2245
2347
  copyPatterns: [],
@@ -2247,25 +2349,46 @@ const TsDocLintPlugin = (options = {})=>{
2247
2349
  "dev",
2248
2350
  "npm"
2249
2351
  ],
2250
- tsconfigPath: void 0,
2251
2352
  externals: [],
2252
- dtsBundledPackages: void 0,
2253
- transformFiles: void 0,
2254
- tsdocLint: void 0
2353
+ apiModel: true
2255
2354
  };
2256
2355
  static mergeOptions(options = {}) {
2257
- const merged = {
2258
- ...NodeLibraryBuilder.DEFAULT_OPTIONS,
2259
- ...options,
2260
- copyPatterns: [
2261
- ...options.copyPatterns ?? NodeLibraryBuilder.DEFAULT_OPTIONS.copyPatterns
2262
- ]
2263
- };
2264
- if (existsSync(join(process.cwd(), "public"))) merged.copyPatterns.unshift({
2356
+ const copyPatterns = [
2357
+ ...options.copyPatterns ?? NodeLibraryBuilder.DEFAULT_OPTIONS.copyPatterns
2358
+ ];
2359
+ if (existsSync(join(process.cwd(), "public"))) copyPatterns.unshift({
2265
2360
  from: "./public",
2266
2361
  to: "./",
2267
2362
  context: process.cwd()
2268
2363
  });
2364
+ const merged = {
2365
+ copyPatterns,
2366
+ plugins: options.plugins ?? NodeLibraryBuilder.DEFAULT_OPTIONS.plugins,
2367
+ define: options.define ?? NodeLibraryBuilder.DEFAULT_OPTIONS.define,
2368
+ tsconfigPath: options.tsconfigPath,
2369
+ format: options.format ?? NodeLibraryBuilder.DEFAULT_OPTIONS.format,
2370
+ targets: options.targets ?? NodeLibraryBuilder.DEFAULT_OPTIONS.targets,
2371
+ externals: options.externals ?? NodeLibraryBuilder.DEFAULT_OPTIONS.externals,
2372
+ apiModel: options.apiModel ?? NodeLibraryBuilder.DEFAULT_OPTIONS.apiModel,
2373
+ ...void 0 !== options.entry && {
2374
+ entry: options.entry
2375
+ },
2376
+ ...void 0 !== options.exportsAsIndexes && {
2377
+ exportsAsIndexes: options.exportsAsIndexes
2378
+ },
2379
+ ...void 0 !== options.dtsBundledPackages && {
2380
+ dtsBundledPackages: options.dtsBundledPackages
2381
+ },
2382
+ ...void 0 !== options.transformFiles && {
2383
+ transformFiles: options.transformFiles
2384
+ },
2385
+ ...void 0 !== options.transform && {
2386
+ transform: options.transform
2387
+ },
2388
+ ...void 0 !== options.virtualEntries && {
2389
+ virtualEntries: options.virtualEntries
2390
+ }
2391
+ };
2269
2392
  return merged;
2270
2393
  }
2271
2394
  static create(options = {}) {
@@ -2280,40 +2403,62 @@ const TsDocLintPlugin = (options = {})=>{
2280
2403
  const options = NodeLibraryBuilder.mergeOptions(opts);
2281
2404
  const VERSION = await packageJsonVersion();
2282
2405
  const plugins = [];
2283
- if (options.tsdocLint) {
2284
- const lintOptions = true === options.tsdocLint ? {} : options.tsdocLint;
2285
- if (!lintOptions.tsdoc && "object" == typeof options.apiModel && options.apiModel.tsdoc) lintOptions.tsdoc = options.apiModel.tsdoc;
2406
+ const apiModelConfig = "object" == typeof options.apiModel ? options.apiModel : {};
2407
+ const tsdocConfig = apiModelConfig.tsdoc;
2408
+ const lintConfig = tsdocConfig?.lint;
2409
+ const lintEnabled = false !== options.apiModel && false !== lintConfig;
2410
+ if (lintEnabled) {
2411
+ const { lint: _lint, ...tsdocWithoutLint } = tsdocConfig ?? {};
2412
+ const lintOptions = {
2413
+ ...tsdocConfig && Object.keys(tsdocWithoutLint).length > 0 && {
2414
+ tsdoc: tsdocWithoutLint
2415
+ },
2416
+ ..."object" == typeof lintConfig ? lintConfig : {}
2417
+ };
2286
2418
  plugins.push(TsDocLintPlugin(lintOptions));
2287
2419
  }
2288
- if (!options.entry) plugins.push(AutoEntryPlugin({
2420
+ if (!options.entry) plugins.push(AutoEntryPlugin(null != options.exportsAsIndexes ? {
2289
2421
  exportsAsIndexes: options.exportsAsIndexes
2290
- }));
2422
+ } : void 0));
2291
2423
  const userTransform = options.transform;
2292
2424
  const transformFn = userTransform ? (pkg)=>userTransform({
2293
2425
  target,
2294
2426
  pkg
2295
2427
  }) : void 0;
2428
+ const libraryFormat = options.format ?? "esm";
2296
2429
  plugins.push(PackageJsonTransformPlugin({
2297
2430
  forcePrivate: "dev" === target,
2298
2431
  bundle: true,
2299
2432
  target,
2300
- transform: transformFn
2433
+ format: libraryFormat,
2434
+ ...transformFn && {
2435
+ transform: transformFn
2436
+ }
2301
2437
  }));
2302
2438
  plugins.push(FilesArrayPlugin({
2303
2439
  target,
2304
- transformFiles: options.transformFiles
2440
+ ...options.transformFiles && {
2441
+ transformFiles: options.transformFiles
2442
+ }
2305
2443
  }));
2306
2444
  if (options.plugins) plugins.push(...options.plugins);
2307
2445
  const outputDir = `dist/${target}`;
2308
2446
  const entry = options.entry;
2309
2447
  const apiModelForTarget = "npm" === target ? options.apiModel : void 0;
2310
2448
  plugins.push(DtsPlugin({
2311
- tsconfigPath: options.tsconfigPath,
2449
+ ...options.tsconfigPath && {
2450
+ tsconfigPath: options.tsconfigPath
2451
+ },
2312
2452
  abortOnError: true,
2313
2453
  bundle: true,
2314
- bundledPackages: options.dtsBundledPackages,
2454
+ ...options.dtsBundledPackages && {
2455
+ bundledPackages: options.dtsBundledPackages
2456
+ },
2315
2457
  buildTarget: target,
2316
- apiModel: apiModelForTarget
2458
+ format: libraryFormat,
2459
+ ...void 0 !== apiModelForTarget && {
2460
+ apiModel: apiModelForTarget
2461
+ }
2317
2462
  }));
2318
2463
  const lib = {
2319
2464
  id: target,
@@ -2329,29 +2474,97 @@ const TsDocLintPlugin = (options = {})=>{
2329
2474
  copy: {
2330
2475
  patterns: options.copyPatterns
2331
2476
  },
2332
- externals: options.externals && options.externals.length > 0 ? options.externals : void 0
2477
+ ...options.externals && options.externals.length > 0 && {
2478
+ externals: options.externals
2479
+ }
2333
2480
  },
2334
- format: "esm",
2481
+ format: libraryFormat,
2335
2482
  experiments: {
2336
- advancedEsm: true
2483
+ advancedEsm: "esm" === libraryFormat
2337
2484
  },
2338
2485
  bundle: true,
2339
2486
  plugins,
2340
2487
  source: {
2341
- tsconfigPath: options.tsconfigPath,
2342
- entry,
2488
+ ...options.tsconfigPath && {
2489
+ tsconfigPath: options.tsconfigPath
2490
+ },
2491
+ ...entry && {
2492
+ entry
2493
+ },
2343
2494
  define: {
2344
2495
  "process.env.__PACKAGE_VERSION__": JSON.stringify(VERSION),
2345
2496
  ...options.define
2346
2497
  }
2347
2498
  }
2348
2499
  };
2500
+ const hasRegularEntries = void 0 !== options.entry || NodeLibraryBuilder.packageHasExports();
2501
+ const virtualEntries = options.virtualEntries ?? {};
2502
+ const hasVirtualEntries = Object.keys(virtualEntries).length > 0;
2503
+ if (!hasRegularEntries && !hasVirtualEntries) throw new Error("No entry points configured. Provide package.json exports, explicit entry option, or virtualEntries.");
2504
+ const libConfigs = [];
2505
+ if (hasRegularEntries) libConfigs.push(lib);
2506
+ if (hasVirtualEntries) {
2507
+ const virtualByFormat = new Map();
2508
+ for (const [outputName, config] of Object.entries(virtualEntries)){
2509
+ const entryFormat = config.format ?? libraryFormat;
2510
+ if (!virtualByFormat.has(entryFormat)) virtualByFormat.set(entryFormat, new Map());
2511
+ const entryName = outputName.replace(/\.(c|m)?js$/, "");
2512
+ const formatMap = virtualByFormat.get(entryFormat);
2513
+ if (formatMap) formatMap.set(entryName, config.source);
2514
+ }
2515
+ for (const [format, entries] of virtualByFormat){
2516
+ const virtualEntryNames = new Set(entries.keys());
2517
+ const entryMap = Object.fromEntries(entries);
2518
+ const virtualLib = {
2519
+ id: `${target}-virtual-${format}`,
2520
+ format,
2521
+ bundle: true,
2522
+ output: {
2523
+ target: "node",
2524
+ cleanDistPath: false,
2525
+ sourceMap: false,
2526
+ distPath: {
2527
+ root: outputDir
2528
+ },
2529
+ ...options.externals && options.externals.length > 0 && {
2530
+ externals: options.externals
2531
+ }
2532
+ },
2533
+ source: {
2534
+ entry: entryMap
2535
+ },
2536
+ plugins: [
2537
+ VirtualEntryPlugin({
2538
+ virtualEntryNames
2539
+ }),
2540
+ FilesArrayPlugin({
2541
+ target
2542
+ })
2543
+ ]
2544
+ };
2545
+ libConfigs.push(virtualLib);
2546
+ }
2547
+ if (hasRegularEntries) {
2548
+ const allVirtualEntryNames = new Set();
2549
+ for (const outputName of Object.keys(virtualEntries)){
2550
+ const entryName = outputName.replace(/\.(c|m)?js$/, "");
2551
+ allVirtualEntryNames.add(entryName);
2552
+ }
2553
+ plugins.push({
2554
+ name: "virtual-entry-names-exposer",
2555
+ setup (api) {
2556
+ api.expose("virtual-entry-names", allVirtualEntryNames);
2557
+ }
2558
+ });
2559
+ lib.plugins = plugins;
2560
+ }
2561
+ }
2349
2562
  return defineConfig({
2350
- lib: [
2351
- lib
2352
- ],
2353
- source: {
2354
- tsconfigPath: options.tsconfigPath
2563
+ lib: libConfigs,
2564
+ ...options.tsconfigPath && {
2565
+ source: {
2566
+ tsconfigPath: options.tsconfigPath
2567
+ }
2355
2568
  },
2356
2569
  performance: {
2357
2570
  buildCache: {
@@ -2360,5 +2573,15 @@ const TsDocLintPlugin = (options = {})=>{
2360
2573
  }
2361
2574
  });
2362
2575
  }
2576
+ static packageHasExports() {
2577
+ try {
2578
+ const packageJsonPath = join(process.cwd(), "package.json");
2579
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
2580
+ const { exports } = packageJson;
2581
+ return null != exports && "object" == typeof exports && Object.keys(exports).length > 0;
2582
+ } catch {
2583
+ return false;
2584
+ }
2585
+ }
2363
2586
  }
2364
- export { AutoEntryPlugin, DtsPlugin, EntryExtractor, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, TsDocConfigBuilder, TsDocLintPlugin, TsconfigResolver, TsconfigResolverError, extractEntriesFromPackageJson };
2587
+ export { AutoEntryPlugin, DtsPlugin, EntryExtractor, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, TsDocConfigBuilder, TsDocLintPlugin, TsconfigResolver, TsconfigResolverError, VirtualEntryPlugin, extractEntriesFromPackageJson };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/rslib-builder",
3
- "version": "0.8.1",
3
+ "version": "0.10.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": [
@@ -50,11 +50,11 @@
50
50
  "deep-equal": "^2.2.3",
51
51
  "eslint": "^9.39.2",
52
52
  "eslint-plugin-tsdoc": "^0.5.0",
53
- "glob": "^13.0.0",
53
+ "glob": "^13.0.1",
54
54
  "picocolors": "^1.1.1",
55
55
  "sort-package-json": "^3.6.1",
56
56
  "tmp": "^0.2.5",
57
- "workspace-tools": "^0.40.4",
57
+ "workspace-tools": "^0.41.0",
58
58
  "yaml": "^2.8.2"
59
59
  },
60
60
  "peerDependencies": {
@@ -13,6 +13,7 @@
13
13
  "incremental": true,
14
14
  "isolatedDeclarations": false,
15
15
  "isolatedModules": true,
16
+ "exactOptionalPropertyTypes": true,
16
17
  "jsx": "preserve",
17
18
  "lib": ["esnext"],
18
19
  "module": "nodenext",