@savvy-web/rslib-builder 0.16.0 → 0.18.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.
Files changed (3) hide show
  1. package/index.d.ts +66 -26
  2. package/index.js +57 -21
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -135,6 +135,32 @@ export declare interface ApiModelOptions {
135
135
  * `"include"` otherwise
136
136
  */
137
137
  forgottenExports?: "include" | "error" | "ignore";
138
+ /**
139
+ * Declarative rules for suppressing specific API Extractor warning messages.
140
+ *
141
+ * @remarks
142
+ * Each rule matches on `messageId` (exact), `pattern` (RegExp with substring fallback),
143
+ * or both (AND logic). Suppression is evaluated before `forgottenExports` and TSDoc
144
+ * warning handling and takes priority over both.
145
+ *
146
+ * After each entry, the number of suppressed messages is logged at info level.
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * import type { ApiModelOptions } from '@savvy-web/rslib-builder';
151
+ *
152
+ * const apiModel: ApiModelOptions = {
153
+ * forgottenExports: 'error',
154
+ * suppressWarnings: [
155
+ * // Suppress forgotten-export for Effect _base symbols
156
+ * { messageId: 'ae-forgotten-export', pattern: '_base' },
157
+ * // Suppress a specific TSDoc warning category
158
+ * { messageId: 'tsdoc-param-tag-missing-hyphen' },
159
+ * ],
160
+ * };
161
+ * ```
162
+ */
163
+ suppressWarnings?: WarningSuppressionRule[];
138
164
  }
139
165
 
140
166
  /**
@@ -646,8 +672,6 @@ export declare interface FilesArrayPluginOptions<TMode extends string = string>
646
672
  filesArray: Set<string>;
647
673
  /** Current build mode */
648
674
  mode: TMode;
649
- /** The publish target for this build output, if configured */
650
- target: PublishTarget | undefined;
651
675
  }) => void | Promise<void>;
652
676
  /**
653
677
  * Build mode identifier (e.g., "dev", "npm").
@@ -656,13 +680,6 @@ export declare interface FilesArrayPluginOptions<TMode extends string = string>
656
680
  * Passed to the `transformFiles` callback to allow mode-specific transformations.
657
681
  */
658
682
  mode: TMode;
659
- /**
660
- * The publish target for this build output, if configured.
661
- *
662
- * @remarks
663
- * Passed to the `transformFiles` callback to allow target-specific transformations.
664
- */
665
- target?: PublishTarget;
666
683
  /**
667
684
  * Format directories to include in the files array.
668
685
  * Used in dual format builds so npm's `files` field includes
@@ -1477,8 +1494,6 @@ export declare interface NodeLibraryBuilderOptions {
1477
1494
  filesArray: Set<string>;
1478
1495
  /** Current build mode */
1479
1496
  mode: BuildMode;
1480
- /** The publish target for this build output, if configured */
1481
- target: PublishTarget | undefined;
1482
1497
  }) => void | Promise<void>;
1483
1498
  /**
1484
1499
  * Optional transform function to modify package.json before it's saved.
@@ -2215,15 +2230,15 @@ export declare interface PublishTarget {
2215
2230
  * Plugin to produce per-target output directories for multi-registry publishing.
2216
2231
  *
2217
2232
  * @remarks
2218
- * Runs in `onCloseBuild` after the primary build completes. For each additional
2219
- * publish target (beyond the primary):
2233
+ * Runs in `onCloseBuild` after the main RSlib build completes. For each
2234
+ * publish target:
2220
2235
  *
2221
2236
  * 1. Creates the target directory
2222
- * 2. Copies all build output from the primary output directory
2237
+ * 2. Copies all build output from the staging directory
2223
2238
  * 3. Reads the exposed `base-package-json` (after standard transforms, before user transform)
2224
2239
  * 4. Applies the user transform with the target-specific context
2225
2240
  * 5. Applies optional name override
2226
- * 6. Copies the `files` array from the primary output's package.json
2241
+ * 6. Copies the `files` array from the staging directory's package.json
2227
2242
  * 7. Writes the final package.json to the target directory
2228
2243
  *
2229
2244
  * @param options - Plugin configuration options
@@ -2239,37 +2254,37 @@ export declare const PublishTargetPlugin: (options: PublishTargetPluginOptions)
2239
2254
  */
2240
2255
  export declare interface PublishTargetPluginOptions {
2241
2256
  /**
2242
- * Additional publish targets to write output for (targets beyond the primary).
2257
+ * Publish targets to write output for.
2243
2258
  *
2244
2259
  * @remarks
2245
- * Each target gets a copy of the primary build output with per-target
2260
+ * Each target gets a copy of the build staging output with per-target
2246
2261
  * package.json transformations applied.
2247
2262
  */
2248
- additionalTargets: PublishTarget[];
2263
+ targets: PublishTarget[];
2249
2264
  /**
2250
- * Absolute path to the primary output directory.
2265
+ * Absolute path to the build staging directory (dist/&lt;mode&gt;).
2251
2266
  *
2252
2267
  * @remarks
2253
- * The primary build output is copied from this directory to each
2254
- * additional target's directory.
2268
+ * Build artifacts are copied from this directory to each target's
2269
+ * directory (when they differ).
2255
2270
  */
2256
- primaryOutdir: string;
2271
+ stagingDir: string;
2257
2272
  /**
2258
- * Current build mode (e.g., "npm").
2273
+ * Current build mode.
2259
2274
  */
2260
- mode: string;
2275
+ mode: BuildMode;
2261
2276
  /**
2262
2277
  * Optional user transform function applied to each target's package.json.
2263
2278
  *
2264
2279
  * @remarks
2265
- * Called after copying the base package.json state for each additional target.
2280
+ * Called after copying the base package.json state for each target.
2266
2281
  */
2267
2282
  transform?: TransformPackageJsonFn;
2268
2283
  /**
2269
2284
  * Optional package name override.
2270
2285
  *
2271
2286
  * @remarks
2272
- * When provided, overrides the `name` field in each additional target's package.json.
2287
+ * When provided, overrides the `name` field in each target's package.json.
2273
2288
  */
2274
2289
  name?: string;
2275
2290
  }
@@ -3384,4 +3399,29 @@ export declare class TsconfigResolver {
3384
3399
  virtualEntryNames: Set<string>;
3385
3400
  }
3386
3401
 
3402
+ /**
3403
+ * A declarative rule for suppressing a specific API Extractor warning message.
3404
+ *
3405
+ * @remarks
3406
+ * Both `messageId` and `pattern` must match when both are specified (AND logic).
3407
+ * Specify at least one field per rule — a rule with neither field matches all messages.
3408
+ *
3409
+ * `pattern` is first tried as a RegExp. If the string is not a valid regular
3410
+ * expression it falls back to substring matching.
3411
+ *
3412
+ * @public
3413
+ */
3414
+ export declare interface WarningSuppressionRule {
3415
+ /**
3416
+ * Exact match against the message's `messageId` property
3417
+ * (e.g., `"ae-forgotten-export"`, `"tsdoc-param-tag-missing-hyphen"`).
3418
+ */
3419
+ messageId?: string;
3420
+ /**
3421
+ * Pattern matched against the message's `text` property.
3422
+ * Tried as a RegExp first; falls back to case-sensitive substring match.
3423
+ */
3424
+ pattern?: string;
3425
+ }
3426
+
3387
3427
  export { }
package/index.js CHANGED
@@ -597,6 +597,34 @@ const TSConfigs = {
597
597
  }
598
598
  }
599
599
  };
600
+ function compileRule(rule) {
601
+ let regex = null;
602
+ let literal;
603
+ if (void 0 !== rule.pattern) try {
604
+ regex = new RegExp(rule.pattern);
605
+ } catch {
606
+ literal = rule.pattern;
607
+ }
608
+ return {
609
+ messageId: rule.messageId,
610
+ regex,
611
+ literal
612
+ };
613
+ }
614
+ function matchesCompiled(rule, messageId, text) {
615
+ if (void 0 !== rule.messageId && messageId !== rule.messageId) return false;
616
+ if (null !== rule.regex) return rule.regex.test(text ?? "");
617
+ if (void 0 !== rule.literal) return (text ?? "").includes(rule.literal);
618
+ return true;
619
+ }
620
+ function createMessageSuppressor(rules) {
621
+ const compiled = rules.map(compileRule);
622
+ return {
623
+ matches (messageId, text) {
624
+ return compiled.some((rule)=>matchesCompiled(rule, messageId, text));
625
+ }
626
+ };
627
+ }
600
628
  class TsconfigResolverError extends Error {
601
629
  option;
602
630
  value;
@@ -1120,6 +1148,8 @@ async function bundleDtsFiles(options) {
1120
1148
  const tsdocMetadataOption = "object" == typeof apiModel ? apiModel.tsdocMetadata : void 0;
1121
1149
  const tsdocWarnings = tsdocOptions?.warnings ?? (TsDocConfigBuilder.isCI() ? "fail" : "log");
1122
1150
  const forgottenExports = ("object" == typeof apiModel ? apiModel.forgottenExports : void 0) ?? (TsDocConfigBuilder.isCI() ? "error" : "include");
1151
+ const suppressWarnings = "object" == typeof apiModel ? apiModel.suppressWarnings ?? [] : [];
1152
+ const suppressor = createMessageSuppressor(suppressWarnings);
1123
1153
  const tsdocMetadataEnabled = apiModelEnabled && (void 0 === tsdocMetadataOption || true === tsdocMetadataOption || "object" == typeof tsdocMetadataOption && false !== tsdocMetadataOption.enabled);
1124
1154
  const tsdocMetadataFilename = resolveTsdocMetadataFilename(apiModel);
1125
1155
  getApiExtractorPath();
@@ -1196,10 +1226,16 @@ async function bundleDtsFiles(options) {
1196
1226
  const extractorConfig = ExtractorConfig.prepare(prepareOptions);
1197
1227
  const collectedTsdocWarnings = [];
1198
1228
  const collectedForgottenExports = [];
1229
+ const suppressedMessages = [];
1199
1230
  const extractorResult = Extractor.invoke(extractorConfig, {
1200
1231
  localBuild: true,
1201
1232
  showVerboseMessages: false,
1202
1233
  messageCallback: (message)=>{
1234
+ if (suppressor.matches(message.messageId, message.text)) {
1235
+ message.logLevel = ExtractorLogLevel.None;
1236
+ suppressedMessages.push(`[${message.messageId ?? "unknown"}] ${message.text ?? ""}`);
1237
+ return;
1238
+ }
1203
1239
  if (message.text?.includes("Analysis will use the bundled TypeScript version") || message.text?.includes("The target project appears to use TypeScript")) {
1204
1240
  message.logLevel = ExtractorLogLevel.None;
1205
1241
  return;
@@ -1244,6 +1280,10 @@ async function bundleDtsFiles(options) {
1244
1280
  }
1245
1281
  });
1246
1282
  if (!extractorResult.succeeded) throw new Error(`API Extractor failed for entry "${entryName}"`);
1283
+ if (suppressedMessages.length > 0) {
1284
+ const list = suppressedMessages.map((t)=>` ${picocolors.dim(t)}`).join("\n");
1285
+ core_logger.info(`Suppressed ${suppressedMessages.length} API Extractor message${1 === suppressedMessages.length ? "" : "s"} for entry "${entryName}":\n${list}`);
1286
+ }
1247
1287
  const formatWarning = (warning)=>{
1248
1288
  const location = warning.sourceFilePath ? `${picocolors.cyan(relative(cwd, warning.sourceFilePath))}${warning.sourceFileLine ? `:${warning.sourceFileLine}` : ""}${warning.sourceFileColumn ? `:${warning.sourceFileColumn}` : ""}` : null;
1249
1289
  return location ? `${location}: ${picocolors.yellow(warning.text)}` : picocolors.yellow(warning.text);
@@ -1838,8 +1878,7 @@ const FilesArrayPlugin = (options)=>({
1838
1878
  if (options?.transformFiles) await options.transformFiles({
1839
1879
  compilation: context.compilation,
1840
1880
  filesArray,
1841
- mode: options.mode,
1842
- target: options.target
1881
+ mode: options.mode
1843
1882
  });
1844
1883
  });
1845
1884
  api.processAssets({
@@ -2358,30 +2397,30 @@ const PublishTargetPlugin = (options)=>({
2358
2397
  name: "publish-target-plugin",
2359
2398
  setup (api) {
2360
2399
  api.onCloseBuild(async ()=>{
2361
- const { additionalTargets, primaryOutdir, mode, transform, name } = options;
2362
- if (0 === additionalTargets.length) return;
2400
+ const { targets, stagingDir, mode, transform, name } = options;
2401
+ if (0 === targets.length) return;
2363
2402
  const basePackageJson = api.useExposed("base-package-json");
2364
2403
  if (!basePackageJson) return;
2365
- const primaryPkgPath = join(primaryOutdir, "package.json");
2366
- const primaryPkg = JSON.parse(readFileSync(primaryPkgPath, "utf-8"));
2367
- for (const target of additionalTargets){
2368
- if (target.directory !== primaryOutdir) {
2404
+ const stagingPkgPath = join(stagingDir, "package.json");
2405
+ const stagingPkg = JSON.parse(readFileSync(stagingPkgPath, "utf-8"));
2406
+ for (const target of targets){
2407
+ if (target.directory !== stagingDir) {
2369
2408
  mkdirSync(target.directory, {
2370
2409
  recursive: true
2371
2410
  });
2372
- cpSync(primaryOutdir, target.directory, {
2411
+ cpSync(stagingDir, target.directory, {
2373
2412
  recursive: true
2374
2413
  });
2375
2414
  }
2376
2415
  let targetPkg = JSON.parse(JSON.stringify(basePackageJson));
2377
2416
  if (transform) targetPkg = transform({
2378
- mode: mode,
2417
+ mode,
2379
2418
  target,
2380
2419
  pkg: targetPkg
2381
2420
  });
2382
2421
  if (name) targetPkg.name = name;
2383
- if (primaryPkg.files) targetPkg.files = [
2384
- ...primaryPkg.files
2422
+ if (stagingPkg.files) targetPkg.files = [
2423
+ ...stagingPkg.files
2385
2424
  ];
2386
2425
  writeFileSync(join(target.directory, "package.json"), `${JSON.stringify(targetPkg, null, 2)}\n`);
2387
2426
  }
@@ -2881,11 +2920,11 @@ if (module.exports && module.exports.__esModule && 'default' in module.exports)
2881
2920
  const collapseIndex = bundle || !(options.exportsAsIndexes ?? false);
2882
2921
  const baseOutputDir = `dist/${mode}`;
2883
2922
  const publishTargets = "npm" === mode ? resolvePublishTargets(packageJson, cwd, external_node_path_resolve(cwd, baseOutputDir)) : [];
2884
- const primaryTarget = publishTargets[0];
2923
+ const hasTargets = publishTargets.length > 0;
2885
2924
  const userTransform = options.transform;
2886
- const transformFn = userTransform ? (pkg)=>userTransform({
2925
+ const transformFn = !hasTargets && userTransform ? (pkg)=>userTransform({
2887
2926
  mode,
2888
- target: primaryTarget,
2927
+ target: void 0,
2889
2928
  pkg
2890
2929
  }) : void 0;
2891
2930
  const apiModelForMode = "npm" === mode ? options.apiModel : void 0;
@@ -2931,9 +2970,6 @@ if (module.exports && module.exports.__esModule && 'default' in module.exports)
2931
2970
  }));
2932
2971
  plugins.push(FilesArrayPlugin({
2933
2972
  mode,
2934
- ...primaryTarget && {
2935
- target: primaryTarget
2936
- },
2937
2973
  ...options.transformFiles && {
2938
2974
  transformFiles: options.transformFiles
2939
2975
  },
@@ -3213,9 +3249,9 @@ if (module.exports && module.exports.__esModule && 'default' in module.exports)
3213
3249
  });
3214
3250
  }
3215
3251
  }
3216
- if (publishTargets.length > 1) plugins.push(PublishTargetPlugin({
3217
- additionalTargets: publishTargets.slice(1),
3218
- primaryOutdir: external_node_path_resolve(cwd, baseOutputDir),
3252
+ if (hasTargets) plugins.push(PublishTargetPlugin({
3253
+ targets: publishTargets,
3254
+ stagingDir: external_node_path_resolve(cwd, baseOutputDir),
3219
3255
  mode,
3220
3256
  ...userTransform && {
3221
3257
  transform: userTransform
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/rslib-builder",
3
- "version": "0.16.0",
3
+ "version": "0.18.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": [