fix(cli): detect @playwright/test version from lockfile#1342
Merged
sorccu merged 5 commits intoJun 5, 2026
Conversation
Add the ability to resolve a single package's version from a project's lockfile, scoped to a workspace importer, for every supported package manager. Each PackageManagerDetector gains parsePackageVersionFromLockfile, backed by per-format parsers in lockfile-package-version.ts: - npm/cnpm: package-lock.json v2/v3 packages map (node_modules path walk) - pnpm: pnpm-lock.yaml importers (scalar v5 / map v6/v9, peer-suffix strip) - bun: bun.lock (JSONC; member-name-scoped packages entries) - yarn classic: custom line parser; yarn berry: YAML descriptors Adds the `yaml` dependency for the YAML-based formats. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ules The Playwright version uploaded to the cloud was read from the locally installed @playwright/test, which can drift from the project's lockfile (e.g. after switching branches without reinstalling). Resolve it from the workspace lockfile instead — the version CI and other developers get — scoped to the workspace member that owns the Playwright config. resolvePlaywrightVersion prefers the lockfile and falls back to the old node_modules read when no lockfile answer is available (no/unsupported lockfile, package not pinned). Warns when a package.json range no longer satisfies the lockfile version (stale lockfile). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getPlaywrightVersionFromPackage now uses the shared PLAYWRIGHT_TEST constant for the resolve specifier and the playwrightRange helper for the declared range, instead of repeating the '@playwright/test' literal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lockfile version detection only checked the nearest workspace importer and the workspace root. In a legal pnpm layout where a workspace package is physically nested inside another member's directory and declares no @playwright/test, Node resolves the version from the enclosing member, not the root — so the previous logic reported the wrong version (a regression versus the old require.resolve behavior). Resolution now walks the full importer ancestry (config dir → workspace root), mirroring Node's upward module resolution. The query carries an ordered importer list and each format resolves accordingly: npm's physical node_modules-path walk already covers ancestors; pnpm picks the nearest declaring importer; bun probes every member-scoped key before the hoisted fallback (two-phase); yarn tries each ancestor's declared range. The orchestrator realpaths the config dir and workspace root, and anchors the drift warning on the consuming package's range. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…geVersionFromLockfile The method reads the lockfile and resolves a version across the importer chain (mirroring Node's upward resolution), not merely parses — "resolve" reflects that and reserves "parse" for the per-format helpers it delegates to. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <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.
Problem
When deploying a Playwright project, the CLI sends the detected
@playwright/testversion string to the Checkly cloud, which uses it to pick the matching Playwright version when running the checks. That version was read from the locally installed@playwright/test/package.json, which can drift from the project's lockfile — e.g. after switching branches without reinstalling. As a result the reported version (and so the version the cloud runs against) could differ from what CI and other developers resolve.Change
Resolve the version string from the project's lockfile (the source of truth), scoped to the workspace member that owns the Playwright config. Falls back to the previous node_modules read when there's no usable lockfile answer (no/unsupported lockfile, or the package isn't pinned), preserving existing behavior. Warns when a
package.jsonrange no longer satisfies the resolved version (stale lockfile).Resolution mirrors Node's upward module resolution across the full importer ancestry (config dir → workspace root), not just the nearest member + root. This matters in nested workspace layouts (e.g. a pnpm package physically nested inside another member's directory that declares no
@playwright/test): Node resolves the version from the physically-enclosing member, and so do we.A new
parsePackageVersionFromLockfileon eachPackageManagerDetectorextracts the version for an ordered chain of candidate importers, backed by per-format parsers (lockfile-package-version.ts):package-lock.jsonv2/v3packagesmap; single physical node_modules-path walk (covers all ancestors). v1 → fall backpnpm-lock.yamlimporters; nearest declaring importer wins; scalar (v5) or{version}(v6/v9), peer-suffix strippedbun.lock(JSONC); two-phase — every member-scoped key before the hoisted fallback (bun.lockb→ fall back)npm:protocol stripped; per-ancestor declared-range matchAdds the
yamldependency for the YAML-based formats. (Native commands likepnpm list --lockfile-onlywere rejected as primary: the flag is recent-pnpm-only and most PMs' equivalents read node_modules — the very drift being avoided.)Why not snyk-nodejs-lockfile-parser
Too heavy for a CLI. These parsers do exactly one thing — return one package's version for one importer chain.
Testing
All formats verified against real generated lockfiles, including multi-member workspaces with conflicting versions and a member physically nested inside another that declares nothing. 42 unit/integration tests covering: the drift case (lockfile version wins over a differing installed version), the no-lockfile fallback, and the nested-importer resolution for npm, pnpm, and bun (the bun case is a regression guard against a naive first-hit lookup).
🤖 Generated with Claude Code