feat(npm): add pnpm_package rule for pnpm workspace protocol resolution#2897
Open
kirkobyte wants to merge 15 commits into
Open
feat(npm): add pnpm_package rule for pnpm workspace protocol resolution#2897kirkobyte wants to merge 15 commits into
kirkobyte wants to merge 15 commits into
Conversation
Adds a new `pnpm_package` macro that wraps `npm_package` with build-time resolution of pnpm workspace protocols (catalog:, workspace:*, etc.) in package.json, mirroring what `pnpm publish` does before packing. Uses bundled @pnpm/* packages with auto-detection of pnpm 10/11 at runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests catalog extraction, catalog protocol resolution (default and named), version override, and publishable target builds. Fixes action conflict when multiple pnpm_package targets share a Bazel package by scoping the transform output. Updates checked-in bundles to match Bazel-built rollup output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… deps The new @pnpm/* dependencies added by pnpm_publish_tools are now reflected in the generated @npm// workspace defs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aged pnpm The .publish target uses a Starlark rule that resolves both pnpm from @pnpm//:pnpm and npm from the Node.js runtime toolchain, ensuring pnpm publish works in sandboxed environments where pnpm internally shells out to npm. Also removes the unused pnpm_publish.mjs wrapper script. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36116c1bf3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
pnpm publish and npm pack both omit devDependencies from the published package. The transform now does the same. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrite the catalog extraction and package.json transform as simple Node.js scripts with no npm dependencies, eliminating the pnpm_publish_tools workspace package, its rollup bundling pipeline, and all @pnpm/* transitive dependencies from the lockfile. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Handle all pnpm publish package.json transformations:
- Strip publish lifecycle scripts (prepublishOnly, prepare, etc.)
- Remove pnpm-specific fields (pnpm, packageManager)
- Promote publishConfig fields to top-level (full whitelist)
- Clean up publishConfig if empty after promotion
- Normalize bin (string → object keyed by unscoped package name)
- Normalize repository (string → {type, url} object)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The @pnpm/* dependencies are no longer pulled in, so the lockfile and generated snapshots match main again. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the hand-rolled YAML parser and js_binary with a single yq expression. yq is already a dependency of the repo. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s_binary Remove manual npm toolchain resolution and PATH setup from _pnpm_publish. The @pnpm//:pnpm js_binary already supports include_npm which handles npm availability via its own launcher. Also forward version to upstream npm_package so NpmPackageInfo.version stays aligned with the transformed package.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pnpm_package macro now auto-constructs the :node_modules label from the calling package and passes it to the transform action. The transform script reads package.json files from the node_modules tree to resolve workspace:*/^/~ protocols to concrete versions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of reading workspace package versions from node_modules on disk, extract them from NpmPackageStoreInfo carried by JsInfo on srcs and deps targets. This avoids pulling in all of node_modules and makes workspace protocol resolution automatic from the dependency graph. Also adds a deps attribute so workspace deps not included in srcs can still have their versions resolved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…translate_lock, add PnpmPackageInfo aspect for automatic version resolution - npm_translate_lock now generates both catalogs.json (from pnpm-workspace.yaml catalog/catalogs sections) and workspace_versions.json (from workspace package.json files) in the @npm repo, eliminating the need for separate pnpm_extract_catalogs targets. - pnpm_package defaults to @npm//:catalogs.json and @npm//:workspace_versions.json so no explicit configuration is needed for basic usage. - Added PnpmPackageInfo provider and _collect_workspace_versions_aspect that traverses srcs/deps/src attrs to auto-discover workspace package versions. This ensures consumers resolving workspace: protocols get the correct build-time version even when source package.json uses a placeholder like "0.0.0-development". - Added workspace_deps parameter for explicit version override when the aspect can't reach a dep through the srcs graph. - Added pnpm_git_tag_version example rule for extracting versions from git tags. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix shell injection in pnpm_git_tag_version: pass prefix/fallback/output as positional arguments instead of interpolating into the shell command - Fix stderr/stdout redirect race in test_publish.sh (>file 2>&1) - Rename resolveVersion parameter to avoid shadowing outer `version` variable - Deduplicate aspect-collected deps against explicit workspace_deps - Remove unused loads (js_library, pnpm_package_json_transform) from test BUILD Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
pnpm_package— a Bazel macro for building publishable npm packages in pnpm workspaces. It wrapsnpm_packagewith a build-time transform that resolves pnpm workspace protocols (catalog:,workspace:, etc.) inpackage.json, mirroring whatpnpm publishdoes before packing.Parts of this can be performed solely through
pnpm publish, however dynamic versions of packages (derived from Git, etc.) cannot.What this enables
In a pnpm monorepo, workspace packages may use a
package.jsonlike this:{ "name": "@myorg/components", // Can either be the actual version, or a placeholder, with Bazel rules determining the // actual version e.g. by referencing version from git tags, like `semantic-release` "version": "0.0.0-development", // Points to source for in-IDE typechecking/navigation "main": "./src/index.ts", "types": "./src/index.ts", "exports": { ".": "./src/index.ts" }, // At publish time, pnpm promotes these fields to top-level, // so the published package points to compiled output "publishConfig": { "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" } } }, "dependencies": { // Resolved from the "default" catalog in pnpm-workspace.yaml "react": "catalog:", // Resolved from the named "utils" catalog "lodash": "catalog:utils", // Resolved to the sibling package's current version (e.g. "^1.2.3") "@myorg/utils": "workspace:^" } }pnpm_packagehandles all of these transforms at build time:publishConfigpromotion —exports,main,types, etc. are promoted to top-level fieldscatalog:resolution — resolved frompnpm-workspace.yamlcatalog definitionsworkspace:resolution — resolved to the actual version of the sibling package"0.0.0-development"is replaced with the real version (from a git tag, stamp, or explicit string)devDependencies, publish lifecycle scripts, andpnpm-specific fields are strippedIDE vs build entrypoints
The current making the editor happy advice requires adding
pathstotsconfig.jsonfor the IDE to resolve types for peer packages within the IDE, as the entrypoints will be pointing to compiled paths that do not exist on disk.The provided example is fairly straightforward:
https://github.com/aspect-build/rules_ts/blob/74d54bda208695d7e8992520e560166875cfbce7/examples/simple/tsconfig.json#L8-L10
However if packages under
@myorg/are not in the same directory, or theexportsfrom packages within@myorgdiffer from the folder structure, a more complex set of rules will be required. Making this more difficult to maintain in a distributed way.If we instead follow PNPM's lead in using
publishConfig, we can make this simpler. Separate values can be used formain/module/exports/typesin the rootpackage.jsonandpublishConfig. This allows centrally declaring a separate set of values that should be used by the IDE vs in actual builds.Auto-swapping protocols for versions (
workspace:,catalog:, etc.)Versions to use for
catalog:andworkpsace:dependnecies are generated automatically bynpm_translate_lock. So a minimalpnpm_packagetarget requires no extra setup to automatically find and swap in these versions for both local builds and publish events:Git-tag versioning
To allow packages to use git tags to version themselves, a rule could extract the git tag into a file label and pass that to
pnpm_package'sversionparameter.E.g. with a rule
pnpm_git_tag_versionthat searches for git tags following<prefix>@<semver>we could set up a rule like this, with consuming packages correctly resolving theworkspace:dependency to the actual version.Automatic workspace version resolution via aspect
When workspace packages use placeholder versions like
"0.0.0-development", the staticworkspace_versions.jsonfromnpm_translate_lockwill contain the placeholder. ThePnpmPackageInfoprovider and_collect_workspace_versions_aspectsolve this:pnpm_packagetransform providesPnpmPackageInfocontaining its transformedpackage.json(with the real resolved version)srcstraversessrcs/deps/srcattrs transitively to auto-discoverPnpmPackageInfofrom workspace depsworkspace_versions.json, so consumers resolvingworkspace:^get the correct build-time versionFor deps not reachable via the
srcsgraph, use explicitworkspace_deps:Publishing
With
publishable = True, a.publishtarget is created that invokespnpm publish:Changes
pnpm_packagemacro wrappingnpm_packagewithpnpm_package_json_transformpnpm_package_json_transformrule + Node.js script for protocol resolution,publishConfigpromotion, cleanupPnpmPackageInfoprovider +_collect_workspace_versions_aspectfor automatic workspace version discoverynpm_translate_locknow generatescatalogs.jsonandworkspace_versions.jsonin the@npmrepopnpm_git_tag_versionexample rule for extracting versions from git tags_pnpm_publishrule forpublishabletargetsTest plan
test_extract_catalogs— verifies catalog extraction from pnpm-workspace.yamltest_pkg— basic catalog + workspace protocol resolutiontest_pkg_versioned— version string overridetest_pkg_version_file— version from file (git tag simulation)test_workspace_deps— explicit workspace_deps overrides placeholder version ("0.0.0-development" → "3.2.1")test_aspect_auto_discover— aspect auto-discovers PnpmPackageInfo through dep graphtest_publish— publishable target creates working launcher script🤖 Generated with Claude Code