@savvy-web/rslib-builder 0.8.0 → 0.9.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/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
  }
@@ -1108,7 +1105,6 @@ export declare type LiteralUnion<LiteralType, BaseType extends Primitive> = Lite
1108
1105
  * export default NodeLibraryBuilder.create({
1109
1106
  * externals: ['@rslib/core', '@rsbuild/core'],
1110
1107
  * dtsBundledPackages: ['picocolors'],
1111
- * apiModel: true,
1112
1108
  * transform({ target, pkg }) {
1113
1109
  * if (target === 'npm') {
1114
1110
  * delete pkg.devDependencies;
@@ -1140,7 +1136,14 @@ export declare class NodeLibraryBuilder {
1140
1136
  * These defaults are merged with user-provided options in {@link NodeLibraryBuilder.mergeOptions}.
1141
1137
  * Arrays are deep-copied to prevent mutation of this object.
1142
1138
  */
1143
- static readonly DEFAULT_OPTIONS: NodeLibraryBuilderOptions;
1139
+ static readonly DEFAULT_OPTIONS: {
1140
+ plugins: never[];
1141
+ define: {};
1142
+ copyPatterns: never[];
1143
+ targets: ("dev" | "npm")[];
1144
+ externals: never[];
1145
+ apiModel: true;
1146
+ };
1144
1147
  /**
1145
1148
  * Merges user-provided options with default options.
1146
1149
  *
@@ -1316,74 +1319,39 @@ export declare interface NodeLibraryBuilderOptions {
1316
1319
  transform?: TransformPackageJsonFn;
1317
1320
  /**
1318
1321
  * Options for API model generation.
1319
- * When enabled, generates an `<unscopedPackageName>.api.json` file in the dist directory.
1322
+ * Generates an `<unscopedPackageName>.api.json` file in the dist directory.
1320
1323
  * Only applies when target is "npm".
1321
1324
  *
1322
1325
  * @remarks
1323
- * The generated API model file contains the full API documentation
1324
- * in a machine-readable format for use by documentation generators.
1326
+ * API model generation is **enabled by default**. The generated file contains
1327
+ * full API documentation in a machine-readable format for documentation generators.
1325
1328
  * The file is emitted to dist but excluded from npm publish (added as negated pattern in `files` array).
1326
1329
  *
1330
+ * @defaultValue true
1331
+ *
1327
1332
  * @example
1328
- * Enable API model generation with defaults:
1333
+ * Disable API model generation:
1329
1334
  * ```typescript
1330
1335
  * import { NodeLibraryBuilder } from '@savvy-web/rslib-builder';
1331
1336
  *
1332
1337
  * export default NodeLibraryBuilder.create({
1333
- * apiModel: true,
1338
+ * apiModel: false,
1334
1339
  * });
1335
1340
  * ```
1336
1341
  *
1337
1342
  * @example
1338
- * Enable with custom filename:
1343
+ * Customize with options (implicitly enabled):
1339
1344
  * ```typescript
1340
1345
  * import { NodeLibraryBuilder } from '@savvy-web/rslib-builder';
1341
1346
  *
1342
1347
  * export default NodeLibraryBuilder.create({
1343
1348
  * apiModel: {
1344
- * enabled: true,
1345
1349
  * filename: 'my-package.api.json',
1346
1350
  * },
1347
1351
  * });
1348
1352
  * ```
1349
1353
  */
1350
1354
  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
1355
  }
1388
1356
 
1389
1357
  /**
@@ -2567,9 +2535,14 @@ export declare class TsconfigResolver {
2567
2535
  *
2568
2536
  * In CI environments (`CI` or `GITHUB_ACTIONS` env vars set to "true"),
2569
2537
  * this method validates the existing file instead of writing, throwing
2570
- * an error if the config is out of date.
2538
+ * an error if the config is out of date. Set `skipCIValidation` to true
2539
+ * to skip this validation (useful when `persistConfig: false`).
2540
+ *
2541
+ * @param options - TSDoc configuration options
2542
+ * @param outputDir - Directory to write the config file
2543
+ * @param skipCIValidation - Skip CI validation even in CI environments
2571
2544
  */
2572
- static writeConfigFile(options: TsDocOptions | undefined, outputDir: string): Promise<string>;
2545
+ static writeConfigFile(options: TsDocOptions | undefined, outputDir: string, skipCIValidation?: boolean): Promise<string>;
2573
2546
  /** Converts TSDocTagSyntaxKind enum to string format. */
2574
2547
  private static syntaxKindToString;
2575
2548
  }
@@ -2586,6 +2559,54 @@ export declare class TsconfigResolver {
2586
2559
  */
2587
2560
  export declare type TsDocLintErrorBehavior = "warn" | "error" | "throw";
2588
2561
 
2562
+ /**
2563
+ * Options for TSDoc lint validation (subset of TsDocLintPluginOptions).
2564
+ * TSDoc configuration is inherited from the parent TsDocOptions.
2565
+ *
2566
+ * @public
2567
+ */
2568
+ export declare interface TsDocLintOptions {
2569
+ /**
2570
+ * Override automatic file discovery with explicit file paths or glob patterns.
2571
+ *
2572
+ * @remarks
2573
+ * By default, uses import graph analysis to discover files from your package's exports.
2574
+ * This ensures only public API files are linted.
2575
+ *
2576
+ * @example
2577
+ * ```typescript
2578
+ * lint: {
2579
+ * include: ["src/**\/*.ts", "!**\/*.test.ts"],
2580
+ * }
2581
+ * ```
2582
+ */
2583
+ include?: string[];
2584
+ /**
2585
+ * How to handle TSDoc lint errors.
2586
+ * - `"warn"`: Log warnings but continue the build
2587
+ * - `"error"`: Log errors but continue the build
2588
+ * - `"throw"`: Fail the build with an error
2589
+ *
2590
+ * @defaultValue `"throw"` in CI environments, `"error"` locally
2591
+ */
2592
+ onError?: TsDocLintErrorBehavior;
2593
+ /**
2594
+ * Persist tsdoc.json to disk for tool integration (ESLint, IDEs).
2595
+ * - `true`: Write to project root as "tsdoc.json" (or validate in CI)
2596
+ * - `PathLike`: Write to specified path (or validate in CI)
2597
+ * - `false`: Clean up after linting (not recommended)
2598
+ *
2599
+ * @remarks
2600
+ * In CI environments (`CI` or `GITHUB_ACTIONS` env vars set to "true"),
2601
+ * the config file is validated instead of written. If the existing file
2602
+ * doesn't match the expected configuration, the build fails with an error
2603
+ * instructing the developer to regenerate the file locally.
2604
+ *
2605
+ * @defaultValue `true`
2606
+ */
2607
+ persistConfig?: boolean | PathLike;
2608
+ }
2609
+
2589
2610
  /**
2590
2611
  * Creates a plugin to validate TSDoc comments before build using ESLint with eslint-plugin-tsdoc.
2591
2612
  *
@@ -2815,21 +2836,6 @@ export declare class TsconfigResolver {
2815
2836
  * ```
2816
2837
  */
2817
2838
  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
2839
  /**
2834
2840
  * How to handle TSDoc validation warnings from API Extractor.
2835
2841
  * - `"log"`: Show warnings in the console but continue the build
@@ -2849,6 +2855,42 @@ export declare class TsconfigResolver {
2849
2855
  * `"log"` otherwise
2850
2856
  */
2851
2857
  warnings?: "log" | "fail" | "none";
2858
+ /**
2859
+ * TSDoc lint validation options.
2860
+ * Validates TSDoc comments before the build starts using ESLint.
2861
+ *
2862
+ * @remarks
2863
+ * Lint validation is **enabled by default**. Set to `false` to disable.
2864
+ * When enabled, it uses the parent TSDoc configuration (tagDefinitions, groups, etc.)
2865
+ * for validation rules.
2866
+ *
2867
+ * @defaultValue true
2868
+ *
2869
+ * @example
2870
+ * Disable lint validation:
2871
+ * ```typescript
2872
+ * apiModel: {
2873
+ * tsdoc: {
2874
+ * lint: false,
2875
+ * },
2876
+ * }
2877
+ * ```
2878
+ *
2879
+ * @example
2880
+ * Customize lint behavior:
2881
+ * ```typescript
2882
+ * apiModel: {
2883
+ * tsdoc: {
2884
+ * tagDefinitions: [{ tagName: '@error', syntaxKind: 'inline' }],
2885
+ * lint: {
2886
+ * onError: 'throw',
2887
+ * include: ['src/**\/*.ts'],
2888
+ * },
2889
+ * },
2890
+ * }
2891
+ * ```
2892
+ */
2893
+ lint?: TsDocLintOptions | boolean;
2852
2894
  }
2853
2895
 
2854
2896
  /**
package/index.js CHANGED
@@ -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";
@@ -786,9 +801,9 @@ class TsDocConfigBuilder {
786
801
  strict: true
787
802
  })) 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
803
  }
789
- static async writeConfigFile(options = {}, outputDir) {
804
+ static async writeConfigFile(options = {}, outputDir, skipCIValidation = false) {
790
805
  const configPath = join(outputDir, "tsdoc.json");
791
- if (TsDocConfigBuilder.isCI()) {
806
+ if (TsDocConfigBuilder.isCI() && !skipCIValidation) {
792
807
  await TsDocConfigBuilder.validateConfigFile(options, configPath);
793
808
  return configPath;
794
809
  }
@@ -869,25 +884,27 @@ async function bundleDtsFiles(options) {
869
884
  const bundledFiles = new Map();
870
885
  let apiModelPath;
871
886
  let tsdocMetadataPath;
872
- const apiModelEnabled = true === apiModel || "object" == typeof apiModel && (void 0 === apiModel.enabled || true === apiModel.enabled);
887
+ const apiModelEnabled = true === apiModel || "object" == typeof apiModel;
873
888
  const apiModelFilename = "object" == typeof apiModel && apiModel.filename ? apiModel.filename : "api.json";
874
889
  const tsdocOptions = "object" == typeof apiModel ? apiModel.tsdoc : void 0;
875
890
  const tsdocMetadataOption = "object" == typeof apiModel ? apiModel.tsdocMetadata : void 0;
876
891
  const tsdocWarnings = tsdocOptions?.warnings ?? (TsDocConfigBuilder.isCI() ? "fail" : "log");
877
- const forgottenExports = ("object" == typeof apiModel ? apiModel.forgottenExports : void 0) ?? "include";
892
+ const forgottenExports = ("object" == typeof apiModel ? apiModel.forgottenExports : void 0) ?? (TsDocConfigBuilder.isCI() ? "error" : "include");
878
893
  const tsdocMetadataEnabled = apiModelEnabled && (void 0 === tsdocMetadataOption || true === tsdocMetadataOption || "object" == typeof tsdocMetadataOption && false !== tsdocMetadataOption.enabled);
879
894
  const tsdocMetadataFilename = "object" == typeof tsdocMetadataOption && tsdocMetadataOption.filename ? tsdocMetadataOption.filename : "tsdoc-metadata.json";
880
895
  getApiExtractorPath();
881
- const persistConfig = tsdocOptions?.persistConfig;
896
+ const lintConfig = "object" == typeof tsdocOptions?.lint ? tsdocOptions.lint : void 0;
897
+ const persistConfig = lintConfig?.persistConfig;
882
898
  const tsdocConfigOutputPath = TsDocConfigBuilder.getConfigPath(persistConfig, cwd);
899
+ const skipCIValidation = false === persistConfig;
883
900
  let tsdocConfigPath;
884
901
  let tsdocConfigFile;
885
902
  if (apiModelEnabled) {
886
- tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions ?? {}, dirname(tsdocConfigOutputPath));
903
+ tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions ?? {}, dirname(tsdocConfigOutputPath), skipCIValidation);
887
904
  const { TSDocConfigFile } = await import("@microsoft/tsdoc-config");
888
905
  tsdocConfigFile = TSDocConfigFile.loadForFolder(dirname(tsdocConfigPath));
889
906
  }
890
- const { Extractor, ExtractorConfig } = await import("@microsoft/api-extractor");
907
+ const { Extractor, ExtractorConfig, ExtractorMessage, ExtractorLogLevel } = await import("@microsoft/api-extractor");
891
908
  for (const [entryName, sourcePath] of entryPoints){
892
909
  const normalizedSourcePath = sourcePath.replace(/^\.\//, "");
893
910
  const dtsFileName = normalizedSourcePath.replace(/\.(tsx?|jsx?)$/, ".d.ts");
@@ -910,7 +927,7 @@ async function bundleDtsFiles(options) {
910
927
  const tempApiModelPath = generateApiModel ? join(tempOutputDir, apiModelFilename) : void 0;
911
928
  const generateTsdocMetadata = tsdocMetadataEnabled && "index" === entryName;
912
929
  const tempTsdocMetadataPath = generateTsdocMetadata ? join(tempOutputDir, tsdocMetadataFilename) : void 0;
913
- const extractorConfig = ExtractorConfig.prepare({
930
+ const prepareOptions = {
914
931
  configObject: {
915
932
  projectFolder: cwd,
916
933
  mainEntryPointFilePath: tempDtsPath,
@@ -922,20 +939,29 @@ async function bundleDtsFiles(options) {
922
939
  enabled: true,
923
940
  untrimmedFilePath: tempBundledPath
924
941
  },
925
- docModel: generateApiModel ? {
926
- enabled: true,
927
- apiJsonFilePath: tempApiModelPath
928
- } : void 0,
929
- tsdocMetadata: generateTsdocMetadata ? {
930
- enabled: true,
931
- tsdocMetadataFilePath: tempTsdocMetadataPath
932
- } : void 0,
942
+ ...generateApiModel && {
943
+ docModel: {
944
+ enabled: true,
945
+ ...tempApiModelPath && {
946
+ apiJsonFilePath: tempApiModelPath
947
+ }
948
+ }
949
+ },
950
+ ...generateTsdocMetadata && {
951
+ tsdocMetadata: {
952
+ enabled: true,
953
+ ...tempTsdocMetadataPath && {
954
+ tsdocMetadataFilePath: tempTsdocMetadataPath
955
+ }
956
+ }
957
+ },
933
958
  bundledPackages: bundledPackages
934
959
  },
935
960
  packageJsonFullPath: join(cwd, "package.json"),
936
- configObjectFullPath: void 0,
937
- tsdocConfigFile: tsdocConfigFile
938
- });
961
+ configObjectFullPath: void 0
962
+ };
963
+ if (tsdocConfigFile) prepareOptions.tsdocConfigFile = tsdocConfigFile;
964
+ const extractorConfig = ExtractorConfig.prepare(prepareOptions);
939
965
  const collectedTsdocWarnings = [];
940
966
  const collectedForgottenExports = [];
941
967
  const extractorResult = Extractor.invoke(extractorConfig, {
@@ -943,33 +969,45 @@ async function bundleDtsFiles(options) {
943
969
  showVerboseMessages: false,
944
970
  messageCallback: (message)=>{
945
971
  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";
972
+ message.logLevel = ExtractorLogLevel.None;
947
973
  return;
948
974
  }
949
975
  if (message.text?.includes("You have changed the public API signature")) {
950
- message.logLevel = "none";
976
+ message.logLevel = ExtractorLogLevel.None;
951
977
  return;
952
978
  }
953
979
  const isTsdocMessage = message.messageId?.startsWith("tsdoc-");
954
- if (isTsdocMessage && message.text) if ("none" === tsdocWarnings) message.logLevel = "none";
980
+ if (isTsdocMessage && message.text) if ("none" === tsdocWarnings) message.logLevel = ExtractorLogLevel.None;
955
981
  else {
956
982
  collectedTsdocWarnings.push({
957
983
  text: message.text,
958
- sourceFilePath: message.sourceFilePath,
959
- sourceFileLine: message.sourceFileLine,
960
- sourceFileColumn: message.sourceFileColumn
984
+ ...null != message.sourceFilePath && {
985
+ sourceFilePath: message.sourceFilePath
986
+ },
987
+ ...null != message.sourceFileLine && {
988
+ sourceFileLine: message.sourceFileLine
989
+ },
990
+ ...null != message.sourceFileColumn && {
991
+ sourceFileColumn: message.sourceFileColumn
992
+ }
961
993
  });
962
- message.logLevel = "none";
994
+ message.logLevel = ExtractorLogLevel.None;
963
995
  }
964
- if ("ae-forgotten-export" === message.messageId && message.text) if ("ignore" === forgottenExports) message.logLevel = "none";
996
+ if ("ae-forgotten-export" === message.messageId && message.text) if ("ignore" === forgottenExports) message.logLevel = ExtractorLogLevel.None;
965
997
  else {
966
998
  collectedForgottenExports.push({
967
999
  text: message.text,
968
- sourceFilePath: message.sourceFilePath,
969
- sourceFileLine: message.sourceFileLine,
970
- sourceFileColumn: message.sourceFileColumn
1000
+ ...null != message.sourceFilePath && {
1001
+ sourceFilePath: message.sourceFilePath
1002
+ },
1003
+ ...null != message.sourceFileLine && {
1004
+ sourceFileLine: message.sourceFileLine
1005
+ },
1006
+ ...null != message.sourceFileColumn && {
1007
+ sourceFileColumn: message.sourceFileColumn
1008
+ }
971
1009
  });
972
- message.logLevel = "none";
1010
+ message.logLevel = ExtractorLogLevel.None;
973
1011
  }
974
1012
  }
975
1013
  });
@@ -1009,9 +1047,15 @@ async function bundleDtsFiles(options) {
1009
1047
  }
1010
1048
  return {
1011
1049
  bundledFiles,
1012
- apiModelPath,
1013
- tsdocMetadataPath,
1014
- tsdocConfigPath
1050
+ ...apiModelPath && {
1051
+ apiModelPath
1052
+ },
1053
+ ...tsdocMetadataPath && {
1054
+ tsdocMetadataPath
1055
+ },
1056
+ ...tsdocConfigPath && {
1057
+ tsdocConfigPath
1058
+ }
1015
1059
  };
1016
1060
  }
1017
1061
  function stripSourceMapComment(content) {
@@ -1045,8 +1089,12 @@ function runTsgo(options) {
1045
1089
  const args = generateTsgoArgs({
1046
1090
  configPath,
1047
1091
  declarationDir,
1048
- rootDir,
1049
- tsBuildInfoFile
1092
+ ...rootDir && {
1093
+ rootDir
1094
+ },
1095
+ ...tsBuildInfoFile && {
1096
+ tsBuildInfoFile
1097
+ }
1050
1098
  });
1051
1099
  return new Promise((resolve)=>{
1052
1100
  const child = spawn(tsgoBinPath, args, {
@@ -1098,17 +1146,16 @@ function runTsgo(options) {
1098
1146
  const log = createEnvLogger("dts");
1099
1147
  const cwd = api.context.rootPath;
1100
1148
  try {
1101
- let configTsconfigPath = options.tsconfigPath || config.source?.tsconfigPath;
1102
- if (!configTsconfigPath && options.buildTarget) {
1149
+ let configTsconfigPath = options.tsconfigPath;
1150
+ if (options.buildTarget) {
1103
1151
  const originalCwd = process.cwd();
1104
1152
  try {
1105
1153
  process.chdir(cwd);
1106
1154
  configTsconfigPath = TSConfigs.node.ecma.lib.writeBundleTempConfig(options.buildTarget);
1107
- log.global.info(`Using tsconfig: ${configTsconfigPath}`);
1108
1155
  } finally{
1109
1156
  process.chdir(originalCwd);
1110
1157
  }
1111
- }
1158
+ } else if (!configTsconfigPath) configTsconfigPath = config.source?.tsconfigPath;
1112
1159
  state.tsconfigPath = findTsConfig(cwd, configTsconfigPath);
1113
1160
  if (!state.tsconfigPath) {
1114
1161
  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 +1184,12 @@ function runTsgo(options) {
1137
1184
  const { success, output } = await runTsgo({
1138
1185
  configPath: state.tsconfigPath,
1139
1186
  declarationDir: tempDtsDir,
1140
- rootDir: state.parsedConfig.options.rootDir,
1141
- tsBuildInfoFile: state.parsedConfig.options.tsBuildInfoFile,
1187
+ ...state.parsedConfig.options.rootDir && {
1188
+ rootDir: state.parsedConfig.options.rootDir
1189
+ },
1190
+ ...state.parsedConfig.options.tsBuildInfoFile && {
1191
+ tsBuildInfoFile: state.parsedConfig.options.tsBuildInfoFile
1192
+ },
1142
1193
  name: envId
1143
1194
  });
1144
1195
  if (!success) {
@@ -1218,9 +1269,15 @@ function runTsgo(options) {
1218
1269
  tsconfigPath: state.tsconfigPath,
1219
1270
  bundledPackages: options.bundledPackages || [],
1220
1271
  entryPoints,
1221
- banner: options.banner,
1222
- footer: options.footer,
1223
- apiModel: options.apiModel
1272
+ ...options.banner && {
1273
+ banner: options.banner
1274
+ },
1275
+ ...options.footer && {
1276
+ footer: options.footer
1277
+ },
1278
+ ...void 0 !== options.apiModel && {
1279
+ apiModel: options.apiModel
1280
+ }
1224
1281
  });
1225
1282
  let emittedCount = 0;
1226
1283
  for (const [entryName, tempBundledPath] of bundledFiles){
@@ -1742,7 +1799,10 @@ function applyRslibTransformations(packageJson, originalPackageJson, processTSEx
1742
1799
  private: isPrivate
1743
1800
  };
1744
1801
  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);
1802
+ if (processedManifest.bin) {
1803
+ const transformedBin = transformPackageBin(processedManifest.bin, processTSExports);
1804
+ if (transformedBin) processedManifest.bin = transformedBin;
1805
+ }
1746
1806
  if (originalPackageJson.typesVersions) {
1747
1807
  const transformedTypesVersions = {};
1748
1808
  for (const [version, paths] of Object.entries(originalPackageJson.typesVersions)){
@@ -2091,7 +2151,8 @@ async function runTsDocLint(options, cwd) {
2091
2151
  const persistConfig = options.persistConfig;
2092
2152
  const shouldPersist = TsDocConfigBuilder.shouldPersist(persistConfig);
2093
2153
  const tsdocConfigOutputPath = TsDocConfigBuilder.getConfigPath(persistConfig, cwd);
2094
- const tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions, dirname(tsdocConfigOutputPath));
2154
+ const skipCIValidation = false === persistConfig;
2155
+ const tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions, dirname(tsdocConfigOutputPath), skipCIValidation);
2095
2156
  const eslintModule = await import("eslint");
2096
2157
  const tsParserModule = await import("@typescript-eslint/parser");
2097
2158
  const tsdocPluginModule = await import("eslint-plugin-tsdoc");
@@ -2105,8 +2166,12 @@ async function runTsDocLint(options, cwd) {
2105
2166
  warningCount: 0,
2106
2167
  messages: []
2107
2168
  },
2108
- tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
2109
- discoveryErrors: discovery.errors
2169
+ ...shouldPersist && {
2170
+ tsdocConfigPath
2171
+ },
2172
+ ...discovery.errors.length > 0 && {
2173
+ discoveryErrors: discovery.errors
2174
+ }
2110
2175
  };
2111
2176
  let eslintConfig;
2112
2177
  let filesToLint;
@@ -2189,8 +2254,12 @@ async function runTsDocLint(options, cwd) {
2189
2254
  warningCount,
2190
2255
  messages
2191
2256
  },
2192
- tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
2193
- discoveryErrors: discovery.errors.length > 0 ? discovery.errors : void 0
2257
+ ...shouldPersist && {
2258
+ tsdocConfigPath
2259
+ },
2260
+ ...discovery.errors.length > 0 && {
2261
+ discoveryErrors: discovery.errors
2262
+ }
2194
2263
  };
2195
2264
  }
2196
2265
  async function cleanupTsDocConfig(configPath) {
@@ -2239,7 +2308,6 @@ const TsDocLintPlugin = (options = {})=>{
2239
2308
  "npm"
2240
2309
  ];
2241
2310
  static DEFAULT_OPTIONS = {
2242
- entry: void 0,
2243
2311
  plugins: [],
2244
2312
  define: {},
2245
2313
  copyPatterns: [],
@@ -2247,25 +2315,42 @@ const TsDocLintPlugin = (options = {})=>{
2247
2315
  "dev",
2248
2316
  "npm"
2249
2317
  ],
2250
- tsconfigPath: void 0,
2251
2318
  externals: [],
2252
- dtsBundledPackages: void 0,
2253
- transformFiles: void 0,
2254
- tsdocLint: void 0
2319
+ apiModel: true
2255
2320
  };
2256
2321
  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({
2322
+ const copyPatterns = [
2323
+ ...options.copyPatterns ?? NodeLibraryBuilder.DEFAULT_OPTIONS.copyPatterns
2324
+ ];
2325
+ if (existsSync(join(process.cwd(), "public"))) copyPatterns.unshift({
2265
2326
  from: "./public",
2266
2327
  to: "./",
2267
2328
  context: process.cwd()
2268
2329
  });
2330
+ const merged = {
2331
+ copyPatterns,
2332
+ plugins: options.plugins ?? NodeLibraryBuilder.DEFAULT_OPTIONS.plugins,
2333
+ define: options.define ?? NodeLibraryBuilder.DEFAULT_OPTIONS.define,
2334
+ tsconfigPath: options.tsconfigPath,
2335
+ targets: options.targets ?? NodeLibraryBuilder.DEFAULT_OPTIONS.targets,
2336
+ externals: options.externals ?? NodeLibraryBuilder.DEFAULT_OPTIONS.externals,
2337
+ apiModel: options.apiModel ?? NodeLibraryBuilder.DEFAULT_OPTIONS.apiModel,
2338
+ ...void 0 !== options.entry && {
2339
+ entry: options.entry
2340
+ },
2341
+ ...void 0 !== options.exportsAsIndexes && {
2342
+ exportsAsIndexes: options.exportsAsIndexes
2343
+ },
2344
+ ...void 0 !== options.dtsBundledPackages && {
2345
+ dtsBundledPackages: options.dtsBundledPackages
2346
+ },
2347
+ ...void 0 !== options.transformFiles && {
2348
+ transformFiles: options.transformFiles
2349
+ },
2350
+ ...void 0 !== options.transform && {
2351
+ transform: options.transform
2352
+ }
2353
+ };
2269
2354
  return merged;
2270
2355
  }
2271
2356
  static create(options = {}) {
@@ -2280,14 +2365,23 @@ const TsDocLintPlugin = (options = {})=>{
2280
2365
  const options = NodeLibraryBuilder.mergeOptions(opts);
2281
2366
  const VERSION = await packageJsonVersion();
2282
2367
  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;
2368
+ const apiModelConfig = "object" == typeof options.apiModel ? options.apiModel : {};
2369
+ const tsdocConfig = apiModelConfig.tsdoc;
2370
+ const lintConfig = tsdocConfig?.lint;
2371
+ const lintEnabled = false !== options.apiModel && false !== lintConfig;
2372
+ if (lintEnabled) {
2373
+ const { lint: _lint, ...tsdocWithoutLint } = tsdocConfig ?? {};
2374
+ const lintOptions = {
2375
+ ...tsdocConfig && Object.keys(tsdocWithoutLint).length > 0 && {
2376
+ tsdoc: tsdocWithoutLint
2377
+ },
2378
+ ..."object" == typeof lintConfig ? lintConfig : {}
2379
+ };
2286
2380
  plugins.push(TsDocLintPlugin(lintOptions));
2287
2381
  }
2288
- if (!options.entry) plugins.push(AutoEntryPlugin({
2382
+ if (!options.entry) plugins.push(AutoEntryPlugin(null != options.exportsAsIndexes ? {
2289
2383
  exportsAsIndexes: options.exportsAsIndexes
2290
- }));
2384
+ } : void 0));
2291
2385
  const userTransform = options.transform;
2292
2386
  const transformFn = userTransform ? (pkg)=>userTransform({
2293
2387
  target,
@@ -2297,23 +2391,33 @@ const TsDocLintPlugin = (options = {})=>{
2297
2391
  forcePrivate: "dev" === target,
2298
2392
  bundle: true,
2299
2393
  target,
2300
- transform: transformFn
2394
+ ...transformFn && {
2395
+ transform: transformFn
2396
+ }
2301
2397
  }));
2302
2398
  plugins.push(FilesArrayPlugin({
2303
2399
  target,
2304
- transformFiles: options.transformFiles
2400
+ ...options.transformFiles && {
2401
+ transformFiles: options.transformFiles
2402
+ }
2305
2403
  }));
2306
2404
  if (options.plugins) plugins.push(...options.plugins);
2307
2405
  const outputDir = `dist/${target}`;
2308
2406
  const entry = options.entry;
2309
2407
  const apiModelForTarget = "npm" === target ? options.apiModel : void 0;
2310
2408
  plugins.push(DtsPlugin({
2311
- tsconfigPath: options.tsconfigPath,
2409
+ ...options.tsconfigPath && {
2410
+ tsconfigPath: options.tsconfigPath
2411
+ },
2312
2412
  abortOnError: true,
2313
2413
  bundle: true,
2314
- bundledPackages: options.dtsBundledPackages,
2414
+ ...options.dtsBundledPackages && {
2415
+ bundledPackages: options.dtsBundledPackages
2416
+ },
2315
2417
  buildTarget: target,
2316
- apiModel: apiModelForTarget
2418
+ ...void 0 !== apiModelForTarget && {
2419
+ apiModel: apiModelForTarget
2420
+ }
2317
2421
  }));
2318
2422
  const lib = {
2319
2423
  id: target,
@@ -2329,7 +2433,9 @@ const TsDocLintPlugin = (options = {})=>{
2329
2433
  copy: {
2330
2434
  patterns: options.copyPatterns
2331
2435
  },
2332
- externals: options.externals && options.externals.length > 0 ? options.externals : void 0
2436
+ ...options.externals && options.externals.length > 0 && {
2437
+ externals: options.externals
2438
+ }
2333
2439
  },
2334
2440
  format: "esm",
2335
2441
  experiments: {
@@ -2338,8 +2444,12 @@ const TsDocLintPlugin = (options = {})=>{
2338
2444
  bundle: true,
2339
2445
  plugins,
2340
2446
  source: {
2341
- tsconfigPath: options.tsconfigPath,
2342
- entry,
2447
+ ...options.tsconfigPath && {
2448
+ tsconfigPath: options.tsconfigPath
2449
+ },
2450
+ ...entry && {
2451
+ entry
2452
+ },
2343
2453
  define: {
2344
2454
  "process.env.__PACKAGE_VERSION__": JSON.stringify(VERSION),
2345
2455
  ...options.define
@@ -2350,8 +2460,10 @@ const TsDocLintPlugin = (options = {})=>{
2350
2460
  lib: [
2351
2461
  lib
2352
2462
  ],
2353
- source: {
2354
- tsconfigPath: options.tsconfigPath
2463
+ ...options.tsconfigPath && {
2464
+ source: {
2465
+ tsconfigPath: options.tsconfigPath
2466
+ }
2355
2467
  },
2356
2468
  performance: {
2357
2469
  buildCache: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/rslib-builder",
3
- "version": "0.8.0",
3
+ "version": "0.9.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,7 +50,7 @@
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",
@@ -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",