@savvy-web/rslib-builder 0.10.0 → 0.11.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 (4) hide show
  1. package/README.md +15 -96
  2. package/index.d.ts +70 -5
  3. package/index.js +159 -87
  4. package/package.json +6 -3
package/README.md CHANGED
@@ -8,126 +8,45 @@ Build modern ESM Node.js libraries with minimal configuration. Handles
8
8
  TypeScript declarations, package.json transformations, and PNPM workspace
9
9
  resolution automatically.
10
10
 
11
- Building TypeScript packages for npm involves repetitive setup: configuring
12
- bundlers, generating declarations, transforming package.json exports, and
13
- resolving workspace references. rslib-builder handles these tasks so you can
14
- focus on your code.
15
-
16
11
  ## Features
17
12
 
18
- - **Zero Config** - Auto-detects entry points from package.json exports
19
- - **Fast Type Generation** - Uses tsgo (native TypeScript) for 10-100x faster
20
- declaration generation
21
- - **Bundled Declarations** - Rolls up TypeScript types via API Extractor for
22
- cleaner public APIs, with multi-entry support for packages with multiple exports
23
- - **Multi-Target Builds** - Separate dev (source maps) and npm (optimized)
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
29
- - **PNPM Integration** - Automatically resolves `catalog:` and `workspace:`
30
- references
31
- - **Package.json Transform** - Converts `.ts` exports to `.js`, generates files
32
- array, removes dev-only fields
33
- - **TSDoc Validation** - Pre-build TSDoc validation with automatic public API discovery
34
- - **API Model Generation** - Optional API model and resolved tsconfig output for
35
- documentation tooling
36
- - **Extensible** - Add custom RSlib/Rsbuild plugins for advanced use cases
37
-
38
- ## Prerequisites
39
-
40
- - Node.js 24.x or later
41
- - pnpm 10.x or later
42
- - TypeScript 5.9.x or later
13
+ - **Zero-Config Entry Detection** - Auto-discovers entry points from package.json
14
+ exports, no manual configuration needed
15
+ - **10-100x Faster Types** - Uses tsgo (native TypeScript compiler) with API
16
+ Extractor for bundled, clean public API declarations
17
+ - **Production-Ready Transforms** - Converts `.ts` exports to `.js`, resolves
18
+ PNPM `catalog:` and `workspace:` references, generates files array
19
+ - **Multi-Target Builds** - Separate dev (with source maps) and npm (optimized)
20
+ outputs from a single configuration
21
+ - **TSDoc Validation** - Pre-build documentation validation with automatic
22
+ public API discovery
43
23
 
44
24
  ## Installation
45
25
 
46
26
  ```bash
47
- pnpm add -D @savvy-web/rslib-builder
48
- ```
49
-
50
- ### Peer Dependencies
51
-
52
- Install the required peer dependencies:
53
-
54
- ```bash
55
- pnpm add -D @rslib/core @microsoft/api-extractor @typescript/native-preview
27
+ npm install --save-dev @savvy-web/rslib-builder @rslib/core @microsoft/api-extractor @typescript/native-preview
56
28
  ```
57
29
 
58
30
  ## Quick Start
59
31
 
60
- Extend the provided tsconfig for optimal settings:
61
-
62
- ```jsonc
63
- // tsconfig.json
64
- {
65
- "extends": "@savvy-web/rslib-builder/tsconfig/ecma/lib.json"
66
- }
67
- ```
68
-
69
- Create an `rslib.config.ts` in your project root:
70
-
71
32
  ```typescript
33
+ // rslib.config.ts
72
34
  import { NodeLibraryBuilder } from '@savvy-web/rslib-builder';
73
35
 
74
36
  export default NodeLibraryBuilder.create({
75
37
  externals: ['@rslib/core'],
76
- transform({ pkg, target }) {
77
- if (target === 'npm') {
78
- delete pkg.devDependencies;
79
- }
80
- return pkg;
81
- },
82
38
  });
83
39
  ```
84
40
 
85
- Add scripts to your `package.json`:
86
-
87
- ```json
88
- {
89
- "scripts": {
90
- "build": "rslib build --env-mode dev",
91
- "build:npm": "rslib build --env-mode npm"
92
- }
93
- }
94
- ```
41
+ Build with `rslib build --env-mode dev` or `rslib build --env-mode npm`.
95
42
 
96
43
  ## Documentation
97
44
 
98
- For detailed documentation, see the [docs/](./docs/) directory:
99
-
100
- - [Getting Started](./docs/guides/getting-started.md) - Installation and setup
101
- - [Configuration](./docs/guides/configuration.md) - All options explained
102
- - [Plugin System](./docs/guides/plugins.md) - Built-in and custom plugins
103
- - [Architecture](./docs/architecture/overview.md) - How it works internally
104
- - [Troubleshooting](./docs/troubleshooting.md) - Common issues and solutions
105
-
106
- ## Example
107
-
108
- This package builds itself using its own `NodeLibraryBuilder`. See
109
- [`rslib.config.ts`](./rslib.config.ts) for a production example.
110
-
111
- ## Support
112
-
113
- This software is provided as-is under the MIT License with no warranty or
114
- support guarantees. While we welcome bug reports and feature requests via
115
- GitHub Issues, we cannot guarantee response times or resolution.
116
-
117
- For security vulnerabilities, please see [SECURITY.md](./SECURITY.md).
118
-
119
- ## Links
120
-
121
- - [RSlib Documentation](https://rslib.dev/)
122
- - [Rsbuild Plugin API](https://rsbuild.dev/plugins/dev/core)
123
- - [API Extractor](https://api-extractor.com/)
124
- - [PNPM Workspace](https://pnpm.io/workspaces)
125
- - [PNPM Catalogs](https://pnpm.io/catalogs)
45
+ For configuration options, API reference, and advanced usage, see [docs](./docs/).
126
46
 
127
47
  ## Contributing
128
48
 
129
- Contributions welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup
130
- and guidelines.
49
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
131
50
 
132
51
  ## License
133
52
 
package/index.d.ts CHANGED
@@ -1206,6 +1206,27 @@ export declare class NodeLibraryBuilder {
1206
1206
  }
1207
1207
 
1208
1208
  /**
1209
+ * Configuration options for the NodeLibraryBuilder.
1210
+ *
1211
+ * @remarks
1212
+ * All options are optional with sensible defaults. The most commonly customized options are:
1213
+ * - `externals`: For dependencies that should remain external
1214
+ * - `dtsBundledPackages`: For inlining type definitions
1215
+ * - `transform`: For custom package.json modifications
1216
+ *
1217
+ * @example
1218
+ * ```typescript
1219
+ * import type { NodeLibraryBuilderOptions } from '@savvy-web/rslib-builder';
1220
+ *
1221
+ * const options: NodeLibraryBuilderOptions = {
1222
+ * externals: ['@rslib/core'],
1223
+ * dtsBundledPackages: ['picocolors'],
1224
+ * apiModel: {
1225
+ * localPaths: ['../docs/packages/my-package'],
1226
+ * },
1227
+ * };
1228
+ * ```
1229
+ *
1209
1230
  * @public
1210
1231
  */
1211
1232
  export declare interface NodeLibraryBuilderOptions {
@@ -1287,11 +1308,46 @@ export declare interface NodeLibraryBuilderOptions {
1287
1308
  * ```
1288
1309
  */
1289
1310
  exportsAsIndexes?: boolean;
1311
+ /**
1312
+ * Patterns for files to copy to the output directory.
1313
+ *
1314
+ * @remarks
1315
+ * Supports both string paths and detailed configuration objects.
1316
+ * A `public/` directory in the project root is automatically added if it exists.
1317
+ *
1318
+ * @defaultValue `[]`
1319
+ */
1290
1320
  copyPatterns: (string | CopyPatternConfig)[];
1291
- /** Additional plugins */
1321
+ /**
1322
+ * Additional Rsbuild plugins to include in the build.
1323
+ *
1324
+ * @remarks
1325
+ * These plugins run after the built-in plugins (AutoEntryPlugin, DtsPlugin, etc.).
1326
+ *
1327
+ * @defaultValue `[]`
1328
+ */
1292
1329
  plugins: RsbuildPlugin[];
1330
+ /**
1331
+ * Compile-time constants for code replacement.
1332
+ *
1333
+ * @remarks
1334
+ * Values are stringified and replaced in the source code during bundling.
1335
+ * The `process.env.__PACKAGE_VERSION__` constant is automatically defined.
1336
+ *
1337
+ * @see {@link https://rsbuild.dev/config/source/define | Rsbuild define documentation}
1338
+ *
1339
+ * @defaultValue `{}`
1340
+ */
1293
1341
  define: SourceConfig["define"];
1294
- /** Path to tsconfig for build (default: ./tsconfig.build.json) */
1342
+ /**
1343
+ * Path to the TypeScript configuration file for the build.
1344
+ *
1345
+ * @remarks
1346
+ * If not specified, the plugin searches for `tsconfig.json` in the project root.
1347
+ * A temporary tsconfig is generated for declaration generation regardless of this setting.
1348
+ *
1349
+ * @defaultValue `undefined` (auto-detected)
1350
+ */
1295
1351
  tsconfigPath: string | undefined;
1296
1352
  /** Build targets to include (default: ["dev", "npm"]) */
1297
1353
  targets?: BuildTarget[];
@@ -1872,7 +1928,6 @@ export declare type PackageJson = JsonObject & PackageJson.NodeJsStandard & Pack
1872
1928
  *
1873
1929
  * - Consumes `entrypoints` map from AutoEntryPlugin
1874
1930
  * - Consumes `exportToOutputMap` for exportsAsIndexes mode
1875
- * - Exposes `files-cache` for asset caching
1876
1931
  * - Consumes `use-rollup-types` flag from DtsPlugin
1877
1932
  *
1878
1933
  * @param options - Plugin configuration options
@@ -3078,12 +3133,22 @@ export declare class TsconfigResolver {
3078
3133
 
3079
3134
  /**
3080
3135
  * Options for the VirtualEntryPlugin.
3136
+ *
3137
+ * @remarks
3138
+ * Virtual entries are used for special files that need bundling but should not
3139
+ * generate type declarations or appear in package.json exports. Common use cases
3140
+ * include pnpmfile.cjs or other configuration files.
3141
+ *
3081
3142
  * @public
3082
3143
  */
3083
3144
  export declare interface VirtualEntryPluginOptions {
3084
3145
  /**
3085
- * Set of virtual entry names (without extensions).
3086
- * Used by DtsPlugin to skip type generation for these entries.
3146
+ * Set of virtual entry names (without file extensions).
3147
+ *
3148
+ * @remarks
3149
+ * These names are exposed to DtsPlugin to skip type generation.
3150
+ * For example, if `virtualEntryNames` contains `"pnpmfile"`, the
3151
+ * entry `pnpmfile.cjs` will be bundled but no `pnpmfile.d.ts` will be generated.
3087
3152
  */
3088
3153
  virtualEntryNames: Set<string>;
3089
3154
  }
package/index.js CHANGED
@@ -5,7 +5,7 @@ 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 { getWorkspaceManagerRoot } from "workspace-tools";
8
+ import { getCatalogs, getWorkspaceManagerAndRoot, 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";
@@ -13,8 +13,11 @@ import typescript, { ImportsNotUsedAsValues, JsxEmit, ModuleDetectionKind, Modul
13
13
  import { createRequire } from "node:module";
14
14
  import { inspect } from "node:util";
15
15
  import sort_package_json from "sort-package-json";
16
+ import { getCatalogsFromWorkspaceManifest } from "@pnpm/catalogs.config";
17
+ import { parseCatalogProtocol } from "@pnpm/catalogs.protocol-parser";
16
18
  import { createExportableManifest } from "@pnpm/exportable-manifest";
17
- import { parse } from "yaml";
19
+ import { readWantedLockfile } from "@pnpm/lockfile.fs";
20
+ import { readWorkspaceManifest } from "@pnpm/workspace.read-manifest";
18
21
  const { cyan: cyan, dim: dim, bold: bold } = picocolors;
19
22
  function formatTime(ms) {
20
23
  if (ms < 1000) return `${ms}ms`;
@@ -310,21 +313,6 @@ class TSConfigFile {
310
313
  }
311
314
  }
312
315
  class LibraryTSConfigFile extends TSConfigFile {
313
- bundle(target) {
314
- const config = transformStringsDeep(this.config, (str)=>str.replace("${configDir}", "../../../../../.."));
315
- const include = config.include?.filter((pattern)=>pattern.includes("/src/") || pattern.includes("/types/") || pattern.includes("/public/") || pattern.includes("package.json")).filter((pattern)=>!pattern.includes(".tsx") && !pattern.includes(".cts"));
316
- return {
317
- ...config,
318
- compilerOptions: {
319
- ...config.compilerOptions,
320
- outDir: "dist",
321
- tsBuildInfoFile: `${process.cwd()}/dist/.tsbuildinfo.${target}.bundle`
322
- },
323
- ...void 0 !== include && include.length > 0 ? {
324
- include
325
- } : {}
326
- };
327
- }
328
316
  writeBundleTempConfig(_target) {
329
317
  const cwd = process.cwd();
330
318
  const baseConfig = this.config;
@@ -1576,75 +1564,76 @@ const FilesArrayPlugin = (options)=>({
1576
1564
  });
1577
1565
  const CATALOG_PREFIX = "catalog:";
1578
1566
  const WORKSPACE_PREFIX = "workspace:";
1579
- class PnpmCatalog {
1580
- catalogCache = null;
1581
- catalogCacheMtime = null;
1582
- cachedWorkspaceRoot = null;
1567
+ class WorkspaceCatalog {
1568
+ catalogsCache = null;
1569
+ cachedWorkspaceInfo = null;
1583
1570
  clearCache() {
1584
- this.catalogCache = null;
1585
- this.catalogCacheMtime = null;
1586
- this.cachedWorkspaceRoot = null;
1571
+ this.catalogsCache = null;
1572
+ this.cachedWorkspaceInfo = null;
1587
1573
  }
1588
- async getCatalog() {
1589
- try {
1590
- if (!this.cachedWorkspaceRoot) {
1591
- this.cachedWorkspaceRoot = getWorkspaceManagerRoot(process.cwd());
1592
- if (!this.cachedWorkspaceRoot) throw new Error("Could not find workspace root - ensure you're in a workspace");
1593
- }
1594
- const workspaceFile = external_node_path_resolve(this.cachedWorkspaceRoot, "pnpm-workspace.yaml");
1595
- const stats = await stat(workspaceFile);
1596
- const currentMtime = stats.mtime.getTime();
1597
- if (null !== this.catalogCache && this.catalogCacheMtime === currentMtime) return this.catalogCache;
1598
- const content = await readFile(workspaceFile, "utf-8");
1599
- const workspace = parse(content);
1600
- this.catalogCache = workspace.catalog ?? {};
1601
- this.catalogCacheMtime = currentMtime;
1602
- return this.catalogCache;
1603
- } catch (error) {
1604
- const errorMessage = error instanceof Error ? error.message : String(error);
1574
+ async getCatalogs() {
1575
+ if (null !== this.catalogsCache) return this.catalogsCache;
1576
+ const workspaceInfo = this.getWorkspaceInfo();
1577
+ if (!workspaceInfo) {
1605
1578
  const logger = createEnvLogger("catalog");
1606
- if (errorMessage.includes("ENOENT") && errorMessage.includes("pnpm-workspace.yaml")) {
1607
- logger.error("Failed to read pnpm catalog: workspace configuration not found");
1608
- logger.error(" -> Ensure you're in a pnpm workspace with proper configuration");
1609
- } else if (errorMessage.includes("YAML")) {
1610
- logger.error("Failed to read pnpm catalog: Invalid YAML syntax in workspace configuration");
1611
- logger.error(" -> Check workspace configuration file syntax");
1612
- } else logger.error(`Failed to read pnpm catalog from pnpm-workspace.yaml: ${errorMessage}`);
1579
+ logger.error("Could not find workspace root - ensure you're in a workspace");
1613
1580
  return {};
1614
1581
  }
1582
+ const { manager, root } = workspaceInfo;
1583
+ if ("pnpm" === manager) this.catalogsCache = await this.readPnpmCatalogs(root);
1584
+ else if ("yarn" === manager) this.catalogsCache = this.readYarnCatalogs(root);
1585
+ else this.catalogsCache = {};
1586
+ return this.catalogsCache;
1615
1587
  }
1616
1588
  async resolvePackageJson(packageJson, dir = process.cwd()) {
1617
- const logger = createEnvLogger("pnpm");
1589
+ const workspaceInfo = this.getWorkspaceInfo();
1590
+ const loggerName = workspaceInfo?.manager === "yarn" ? "yarn" : "pnpm";
1591
+ const logger = createEnvLogger(loggerName);
1618
1592
  try {
1619
- const catalog = await this.getCatalog();
1620
- const catalogDeps = this.collectDependencies(packageJson, CATALOG_PREFIX);
1593
+ const catalogs = await this.getCatalogs();
1594
+ const catalogDeps = this.collectCatalogDependencies(packageJson);
1621
1595
  const workspaceDeps = this.collectDependencies(packageJson, WORKSPACE_PREFIX);
1622
1596
  const hasCatalogDeps = catalogDeps.length > 0;
1623
1597
  const hasWorkspaceDeps = workspaceDeps.length > 0;
1624
- if (hasCatalogDeps && 0 === Object.keys(catalog).length) {
1625
- const error = `Package contains ${CATALOG_PREFIX} dependencies but catalog configuration is missing`;
1626
- logger.error(error);
1627
- logger.error(" -> Catalog dependencies found:");
1628
- for (const { field, dependency, version } of catalogDeps)logger.error(` - ${field}.${dependency}: ${version}`);
1629
- throw new Error(error);
1598
+ if (hasCatalogDeps) {
1599
+ const missingCatalogs = this.findMissingCatalogs(catalogDeps, catalogs);
1600
+ if (missingCatalogs.size > 0) {
1601
+ const available = Object.keys(catalogs).join(", ") || "none";
1602
+ const error = `Catalog(s) not found: ${[
1603
+ ...missingCatalogs
1604
+ ].join(", ")}. Available: ${available}`;
1605
+ logger.error(error);
1606
+ logger.error(" -> Catalog dependencies found:");
1607
+ for (const { field, dependency, version, catalogName } of catalogDeps)if (missingCatalogs.has(catalogName)) logger.error(` - ${field}.${dependency}: ${version}`);
1608
+ throw new Error(error);
1609
+ }
1610
+ logger.info(`Resolving ${catalogDeps.length} ${CATALOG_PREFIX} dependencies`);
1630
1611
  }
1631
- if (hasCatalogDeps) logger.info(`Resolving ${catalogDeps.length} ${CATALOG_PREFIX} dependencies`);
1632
1612
  if (hasWorkspaceDeps) logger.info(`Resolving ${workspaceDeps.length} ${WORKSPACE_PREFIX} dependencies`);
1633
1613
  const result = await createExportableManifest(dir, packageJson, {
1634
- catalogs: {
1635
- default: catalog
1636
- }
1614
+ catalogs
1637
1615
  });
1638
- if (hasCatalogDeps || hasWorkspaceDeps) this.logResolvedDependencies(result, [
1639
- ...catalogDeps,
1640
- ...workspaceDeps
1641
- ], logger);
1616
+ if (hasCatalogDeps || hasWorkspaceDeps) {
1617
+ const allDeps = [
1618
+ ...catalogDeps.map((d)=>({
1619
+ field: d.field,
1620
+ dependency: d.dependency,
1621
+ source: "default" === d.catalogName ? "catalog:" : `catalog:${d.catalogName}`
1622
+ })),
1623
+ ...workspaceDeps.map((d)=>({
1624
+ field: d.field,
1625
+ dependency: d.dependency,
1626
+ source: "workspace:"
1627
+ }))
1628
+ ];
1629
+ this.logResolvedDependencies(result, allDeps, logger);
1630
+ }
1642
1631
  this.validateNoUnresolvedReferences(result, logger);
1643
1632
  return result;
1644
1633
  } catch (error) {
1645
1634
  const errorMessage = error instanceof Error ? error.message : String(error);
1646
- if (errorMessage.startsWith("Transformation failed:") || errorMessage.includes(`Package contains ${CATALOG_PREFIX} dependencies`)) throw error;
1647
- logger.error(`Failed to apply pnpm transformations for directory ${dir}: ${errorMessage}`);
1635
+ if (errorMessage.startsWith("Transformation failed:") || errorMessage.startsWith("Catalog(s) not found:")) throw error;
1636
+ logger.error(`Failed to apply transformations for directory ${dir}: ${errorMessage}`);
1648
1637
  if (errorMessage.includes("catalog")) {
1649
1638
  logger.error(` -> Catalog resolution failed - check workspace configuration and ${CATALOG_PREFIX} dependencies`);
1650
1639
  throw new Error("Catalog resolution failed");
@@ -1658,8 +1647,89 @@ class PnpmCatalog {
1658
1647
  throw new Error(`Manifest processing failed: ${errorMessage}`);
1659
1648
  }
1660
1649
  logger.error(" -> Cannot proceed with invalid package.json transformations");
1661
- throw new Error(`PNPM transformation failed: ${errorMessage}`);
1650
+ throw new Error(`Transformation failed: ${errorMessage}`);
1651
+ }
1652
+ }
1653
+ getWorkspaceInfo() {
1654
+ if (null !== this.cachedWorkspaceInfo) return this.cachedWorkspaceInfo;
1655
+ const info = getWorkspaceManagerAndRoot(process.cwd());
1656
+ this.cachedWorkspaceInfo = info ?? null;
1657
+ return this.cachedWorkspaceInfo;
1658
+ }
1659
+ async readPnpmCatalogs(workspaceRoot) {
1660
+ const lockfileCatalogs = await this.readPnpmLockfileCatalogs(workspaceRoot);
1661
+ if (Object.keys(lockfileCatalogs).length > 0) return lockfileCatalogs;
1662
+ return this.readPnpmWorkspaceCatalogs(workspaceRoot);
1663
+ }
1664
+ async readPnpmLockfileCatalogs(workspaceRoot) {
1665
+ try {
1666
+ const lockfile = await readWantedLockfile(workspaceRoot, {
1667
+ ignoreIncompatible: true
1668
+ });
1669
+ if (!lockfile?.catalogs) return {};
1670
+ return this.convertLockfileCatalogs(lockfile.catalogs);
1671
+ } catch {
1672
+ return {};
1673
+ }
1674
+ }
1675
+ async readPnpmWorkspaceCatalogs(workspaceRoot) {
1676
+ try {
1677
+ const manifest = await readWorkspaceManifest(workspaceRoot);
1678
+ return getCatalogsFromWorkspaceManifest(manifest);
1679
+ } catch {
1680
+ return {};
1681
+ }
1682
+ }
1683
+ readYarnCatalogs(workspaceRoot) {
1684
+ const catalogs = getCatalogs(workspaceRoot, "yarn");
1685
+ if (!catalogs) return {};
1686
+ return this.convertWorkspaceToolsCatalogs(catalogs);
1687
+ }
1688
+ convertLockfileCatalogs(snapshots) {
1689
+ const result = {};
1690
+ for (const [name, entries] of Object.entries(snapshots)){
1691
+ result[name] = {};
1692
+ for (const [dep, entry] of Object.entries(entries))result[name][dep] = entry.specifier;
1693
+ }
1694
+ return result;
1695
+ }
1696
+ convertWorkspaceToolsCatalogs(catalogs) {
1697
+ const result = {};
1698
+ if (catalogs.default) result.default = {
1699
+ ...catalogs.default
1700
+ };
1701
+ if (catalogs.named) for (const [name, catalog] of Object.entries(catalogs.named))result[name] = {
1702
+ ...catalog
1703
+ };
1704
+ return result;
1705
+ }
1706
+ collectCatalogDependencies(packageJson) {
1707
+ const deps = [];
1708
+ const fields = [
1709
+ "dependencies",
1710
+ "devDependencies",
1711
+ "peerDependencies",
1712
+ "optionalDependencies"
1713
+ ];
1714
+ for (const field of fields){
1715
+ const fieldDeps = packageJson[field];
1716
+ if (fieldDeps) for (const [dependency, version] of Object.entries(fieldDeps)){
1717
+ if ("string" != typeof version || !version.startsWith(CATALOG_PREFIX)) continue;
1718
+ const catalogName = parseCatalogProtocol(version);
1719
+ if (catalogName) deps.push({
1720
+ field,
1721
+ dependency,
1722
+ version,
1723
+ catalogName
1724
+ });
1725
+ }
1662
1726
  }
1727
+ return deps;
1728
+ }
1729
+ findMissingCatalogs(deps, catalogs) {
1730
+ const missing = new Set();
1731
+ for (const { catalogName } of deps)if (!catalogs[catalogName]) missing.add(catalogName);
1732
+ return missing;
1663
1733
  }
1664
1734
  collectDependencies(packageJson, prefix) {
1665
1735
  const deps = [];
@@ -1683,28 +1753,35 @@ class PnpmCatalog {
1683
1753
  }
1684
1754
  logResolvedDependencies(resultPkg, originalDeps, logger) {
1685
1755
  const allResolved = {};
1686
- for (const { field, dependency } of originalDeps){
1756
+ for (const { field, dependency, source } of originalDeps){
1687
1757
  const deps = resultPkg[field];
1688
1758
  if (deps?.[dependency]) {
1689
1759
  if (!allResolved[field]) allResolved[field] = [];
1690
1760
  allResolved[field].push({
1691
1761
  dependency,
1692
- version: deps[dependency]
1762
+ version: deps[dependency],
1763
+ source
1693
1764
  });
1694
1765
  }
1695
1766
  }
1696
1767
  if (Object.keys(allResolved).length > 0) {
1697
- logger.info("Resolved dependencies:");
1768
+ logger.global.info("Resolved dependencies:");
1698
1769
  for (const [field, deps] of Object.entries(allResolved)){
1699
- logger.info(`- ${field}:`);
1700
- for (const { dependency, version } of deps)logger.info(` ${dependency}: ${version}`);
1770
+ logger.global.info(`- ${field}:`);
1771
+ for (const { dependency, version, source } of deps)logger.global.info(` ${dependency}: ${version} (${source})`);
1701
1772
  }
1702
1773
  }
1703
1774
  }
1704
1775
  validateNoUnresolvedReferences(resultPkg, logger) {
1776
+ const unresolvedCatalog = this.collectCatalogDependencies(resultPkg);
1777
+ const unresolvedWorkspace = this.collectDependencies(resultPkg, WORKSPACE_PREFIX);
1705
1778
  const unresolvedDeps = [
1706
- ...this.collectDependencies(resultPkg, CATALOG_PREFIX),
1707
- ...this.collectDependencies(resultPkg, WORKSPACE_PREFIX)
1779
+ ...unresolvedCatalog.map((d)=>({
1780
+ field: d.field,
1781
+ dependency: d.dependency,
1782
+ version: d.version
1783
+ })),
1784
+ ...unresolvedWorkspace
1708
1785
  ];
1709
1786
  if (unresolvedDeps.length > 0) {
1710
1787
  const catalogRefs = unresolvedDeps.filter((dep)=>dep.version.startsWith(CATALOG_PREFIX));
@@ -1721,10 +1798,8 @@ class PnpmCatalog {
1721
1798
  }
1722
1799
  }
1723
1800
  }
1724
- let defaultInstance = null;
1725
- function getDefaultPnpmCatalog() {
1726
- if (!defaultInstance) defaultInstance = new PnpmCatalog();
1727
- return defaultInstance;
1801
+ function createWorkspaceCatalog() {
1802
+ return new WorkspaceCatalog();
1728
1803
  }
1729
1804
  function transformExportPath(path, processTSExports = true, collapseIndex = false) {
1730
1805
  let transformedPath = path;
@@ -1829,8 +1904,9 @@ function applyRslibTransformations(packageJson, originalPackageJson, processTSEx
1829
1904
  });
1830
1905
  return sort_package_json(processedManifest);
1831
1906
  }
1832
- async function applyPnpmTransformations(packageJson, dir = process.cwd()) {
1833
- return getDefaultPnpmCatalog().resolvePackageJson(packageJson, dir);
1907
+ async function applyPnpmTransformations(packageJson, dir = process.cwd(), catalog) {
1908
+ const workspaceCatalog = catalog ?? createWorkspaceCatalog();
1909
+ return workspaceCatalog.resolvePackageJson(packageJson, dir);
1834
1910
  }
1835
1911
  async function buildPackageJson(packageJson, isProduction = false, processTSExports = true, entrypoints, exportToOutputMap, bundle, transform) {
1836
1912
  let result;
@@ -1841,12 +1917,9 @@ async function buildPackageJson(packageJson, isProduction = false, processTSExpo
1841
1917
  if (transform) result = transform(result);
1842
1918
  return result;
1843
1919
  }
1844
- const PackageJsonTransformPlugin = (options = {})=>{
1845
- const cache = new Map();
1846
- return {
1920
+ const PackageJsonTransformPlugin = (options = {})=>({
1847
1921
  name: "package-json-processor",
1848
1922
  setup (api) {
1849
- api.expose("files-cache", cache);
1850
1923
  let filesArray = api.useExposed("files-array");
1851
1924
  if (!filesArray) {
1852
1925
  filesArray = new Set();
@@ -1895,8 +1968,7 @@ const PackageJsonTransformPlugin = (options = {})=>{
1895
1968
  }
1896
1969
  });
1897
1970
  }
1898
- };
1899
- };
1971
+ });
1900
1972
  class ImportGraph {
1901
1973
  options;
1902
1974
  sys;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/rslib-builder",
3
- "version": "0.10.0",
3
+ "version": "0.11.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": [
@@ -45,7 +45,11 @@
45
45
  "dependencies": {
46
46
  "@microsoft/tsdoc": "^0.16.0",
47
47
  "@microsoft/tsdoc-config": "^0.18.0",
48
+ "@pnpm/catalogs.config": "^1000.0.5",
49
+ "@pnpm/catalogs.protocol-parser": "^1001.0.0",
48
50
  "@pnpm/exportable-manifest": "^1000.3.1",
51
+ "@pnpm/lockfile.fs": "^1001.1.29",
52
+ "@pnpm/workspace.read-manifest": "^1000.2.10",
49
53
  "@typescript-eslint/parser": "^8.53.1",
50
54
  "deep-equal": "^2.2.3",
51
55
  "eslint": "^9.39.2",
@@ -54,8 +58,7 @@
54
58
  "picocolors": "^1.1.1",
55
59
  "sort-package-json": "^3.6.1",
56
60
  "tmp": "^0.2.5",
57
- "workspace-tools": "^0.41.0",
58
- "yaml": "^2.8.2"
61
+ "workspace-tools": "^0.41.0"
59
62
  },
60
63
  "peerDependencies": {
61
64
  "@microsoft/api-extractor": "^7.55.2",