diff --git a/e2e/pnpm_lockfiles/.bazelignore b/e2e/pnpm_lockfiles/.bazelignore index 882c20359e..809b30182f 100644 --- a/e2e/pnpm_lockfiles/.bazelignore +++ b/e2e/pnpm_lockfiles/.bazelignore @@ -15,3 +15,7 @@ v60/projects/c/node_modules v61/projects/a/node_modules v61/projects/b/node_modules v61/projects/c/node_modules +v90/node_modules +v90/projects/a/node_modules +v90/projects/b/node_modules +v90/projects/c/node_modules diff --git a/e2e/pnpm_lockfiles/MODULE.bazel b/e2e/pnpm_lockfiles/MODULE.bazel index f82b2e9bd7..e3c1884b21 100644 --- a/e2e/pnpm_lockfiles/MODULE.bazel +++ b/e2e/pnpm_lockfiles/MODULE.bazel @@ -37,6 +37,7 @@ npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm") "v54", "v60", "v61", + "v90", ] ] @@ -50,5 +51,6 @@ npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm") "v54", "v60", "v61", + "v90", ] ] diff --git a/e2e/pnpm_lockfiles/WORKSPACE b/e2e/pnpm_lockfiles/WORKSPACE index 7959e5203c..7c6dee8b3a 100644 --- a/e2e/pnpm_lockfiles/WORKSPACE +++ b/e2e/pnpm_lockfiles/WORKSPACE @@ -32,6 +32,7 @@ load("@aspect_rules_js//npm:repositories.bzl", "npm_translate_lock") "v54", "v60", "v61", + "v90", ] ] @@ -39,6 +40,7 @@ load("@lock-v53//:repositories.bzl", npm_repositories_v53 = "npm_repositories") load("@lock-v54//:repositories.bzl", npm_repositories_v54 = "npm_repositories") load("@lock-v60//:repositories.bzl", npm_repositories_v60 = "npm_repositories") load("@lock-v61//:repositories.bzl", npm_repositories_v61 = "npm_repositories") +load("@lock-v90//:repositories.bzl", npm_repositories_v90 = "npm_repositories") npm_repositories_v53() @@ -47,3 +49,5 @@ npm_repositories_v54() npm_repositories_v60() npm_repositories_v61() + +npm_repositories_v90() diff --git a/e2e/pnpm_lockfiles/lockfile-test.bzl b/e2e/pnpm_lockfiles/lockfile-test.bzl index 99f4a7f2ac..3bf6e139c7 100644 --- a/e2e/pnpm_lockfiles/lockfile-test.bzl +++ b/e2e/pnpm_lockfiles/lockfile-test.bzl @@ -29,9 +29,10 @@ def lockfile_test(name = "lockfile", node_modules = "node_modules"): ":.aspect_rules_js/node_modules/@aspect-test+a@5.0.2/pkg", ":.aspect_rules_js/node_modules/@aspect-test+a@5.0.2/ref", + # TODO: not generated with pnpm lock v9 # Direct deps with lifecycles - ":.aspect_rules_js/node_modules/@aspect-test+c@2.0.2/lc", - ":.aspect_rules_js/node_modules/@aspect-test+c@2.0.2/pkg_lc", + # ":.aspect_rules_js/node_modules/@aspect-test+c@2.0.2/lc", + # ":.aspect_rules_js/node_modules/@aspect-test+c@2.0.2/pkg_lc", # TODO: differs across lockfile versions # Direct deps from custom registry diff --git a/e2e/pnpm_lockfiles/setup.sh b/e2e/pnpm_lockfiles/setup.sh index 2934b142a2..b816f5882c 100755 --- a/e2e/pnpm_lockfiles/setup.sh +++ b/e2e/pnpm_lockfiles/setup.sh @@ -4,6 +4,7 @@ # 5.4 - pnpm v7.0.0 bumped the lockfile version to 5.4 # 6.0 - pnpm v8.0.0 bumped the lockfile version to 6.0; this included breaking changes # 6.1 - pnpm v8.6.0 bumped the lockfile version to 6.1 +# 9.0 - pnpm v9.0.0 bumped the lockfile version to 9.0 mv v53/pnpm-lock.yaml . && npx pnpm@^6.0 install --lockfile-only && mv pnpm-lock.yaml v53/ mv v54/pnpm-lock.yaml . && npx pnpm@^7.0 install --lockfile-only && mv pnpm-lock.yaml v54/ @@ -12,3 +13,4 @@ mv v54/pnpm-lock.yaml . && npx pnpm@^7.0 install --lockfile-only && mv pnpm-lock # while still presenting minor differences from <8.6.0. mv v60/pnpm-lock.yaml . && npx pnpm@8.5.1 install --lockfile-only && mv pnpm-lock.yaml v60/ mv v61/pnpm-lock.yaml . && npx pnpm@8.6.0 install --lockfile-only && mv pnpm-lock.yaml v61/ +mv v90/pnpm-lock.yaml . && npx pnpm@^9.0 install --lockfile-only && mv pnpm-lock.yaml v90/ diff --git a/e2e/pnpm_lockfiles/v90/BUILD.bazel b/e2e/pnpm_lockfiles/v90/BUILD.bazel new file mode 100644 index 0000000000..e6d1703ef4 --- /dev/null +++ b/e2e/pnpm_lockfiles/v90/BUILD.bazel @@ -0,0 +1,6 @@ +load("@lock-v90//:defs.bzl", "npm_link_all_packages") +load("//:lockfile-test.bzl", "lockfile_test") + +npm_link_all_packages() + +lockfile_test() diff --git a/e2e/pnpm_lockfiles/v90/pnpm-lock.yaml b/e2e/pnpm_lockfiles/v90/pnpm-lock.yaml new file mode 100644 index 0000000000..75a3d7ac29 --- /dev/null +++ b/e2e/pnpm_lockfiles/v90/pnpm-lock.yaml @@ -0,0 +1,159 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@aspect-test/a': + specifier: 5.0.2 + version: 5.0.2 + '@aspect-test/e': + specifier: 1.0.0 + version: 1.0.0 + rollup: + specifier: 3.2.5 + version: 3.2.5 + uvu: + specifier: 0.5.6 + version: 0.5.6 + optionalDependencies: + '@aspect-test/c': + specifier: 2.0.2 + version: 2.0.2 + devDependencies: + '@aspect-test/b': + specifier: 5.0.2 + version: 5.0.2 + '@types/node': + specifier: 16.18.11 + version: 16.18.11 + + projects/a: {} + + projects/b: + dependencies: + '@scoped/a': + specifier: workspace:* + version: link:../a + + projects/c: + dependencies: + '@scoped/a': + specifier: link:../a + version: link:../a + +packages: + + '@aspect-test/a@5.0.2': + resolution: {integrity: sha512-bURS+F0+tS2XPxUPbrqsTZxIre1U5ZglwzDqcOCrU7MbxuRrkO24hesgTMGJldCglwL/tiEGRlvdMndlPgRdNw==} + hasBin: true + + '@aspect-test/b@5.0.2': + resolution: {integrity: sha512-I8wnJV5J0h8ui1O3K6XPq1qGHKopTl/OnvkSfor7uJ9yRCm2Qv6Tf2LsTgR2xzkgiwhA4iBwdYFwecwinF244w==} + hasBin: true + + '@aspect-test/c@2.0.2': + resolution: {integrity: sha512-rMJmd3YBvY7y0jh+2m72TiAhe6dVKjMMNFFVOXFCbM233m7lsG4cq970H1C8rUsc3AcA5E/cEHlxSVffHlHD2Q==} + hasBin: true + + '@aspect-test/d@2.0.0': + resolution: {integrity: sha512-jndwr8pLUfn795uApTcXG/yZ5hV2At1aS/wo5BVLxqlVVgLoOETF/Dp4QOjMHE/SXkXFowz6Hao+WpmzVvAO0A==} + hasBin: true + peerDependencies: + '@aspect-test/c': x.x.x + + '@aspect-test/e@1.0.0': + resolution: {integrity: sha512-GyAxHYKN650db+xnimHnL2LPz65ilmQsVhCasWA7drDNQn/rfmPiEVMzjRiS7m46scXIERaBmiJMzYDf0bIUbA==} + hasBin: true + + '@types/node@16.18.11': + resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==, tarball: https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + rollup@3.2.5: + resolution: {integrity: sha512-/Ha7HhVVofduy+RKWOQJrxe4Qb3xyZo+chcpYiD8SoQa4AG7llhupUtyfKSSrdBM2mWJjhM8wZwmbY23NmlIYw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + uvu@0.5.6: + resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} + engines: {node: '>=8'} + hasBin: true + +snapshots: + + '@aspect-test/a@5.0.2': + dependencies: + '@aspect-test/b': 5.0.2 + '@aspect-test/c': 2.0.2 + '@aspect-test/d': 2.0.0(@aspect-test/c@2.0.2) + + '@aspect-test/b@5.0.2': + dependencies: + '@aspect-test/a': 5.0.2 + '@aspect-test/c': 2.0.2 + '@aspect-test/d': 2.0.0(@aspect-test/c@2.0.2) + + '@aspect-test/c@2.0.2': {} + + '@aspect-test/d@2.0.0(@aspect-test/c@2.0.2)': + dependencies: + '@aspect-test/c': 2.0.2 + + '@aspect-test/e@1.0.0': {} + + '@types/node@16.18.11': {} + + dequal@2.0.3: {} + + diff@5.2.0: {} + + fsevents@2.3.3: + optional: true + + kleur@4.1.5: {} + + mri@1.2.0: {} + + rollup@3.2.5: + optionalDependencies: + fsevents: 2.3.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + uvu@0.5.6: + dependencies: + dequal: 2.0.3 + diff: 5.2.0 + kleur: 4.1.5 + sade: 1.8.1 diff --git a/npm/private/test/utils_tests.bzl b/npm/private/test/utils_tests.bzl index 50e9315b52..f2e0fee57e 100644 --- a/npm/private/test/utils_tests.bzl +++ b/npm/private/test/utils_tests.bzl @@ -65,10 +65,11 @@ def test_version_supported(ctx): utils.assert_lockfile_version(5.4) utils.assert_lockfile_version(6.0) utils.assert_lockfile_version(6.1) + utils.assert_lockfile_version(9.0) msg = utils.assert_lockfile_version(1.2, testonly = True) asserts.equals(env, "npm_translate_lock requires lock_version at least 5.3, but found 1.2. Please upgrade to pnpm v6 or greater.", msg) msg = utils.assert_lockfile_version(99.99, testonly = True) - asserts.equals(env, "npm_translate_lock currently supports a maximum lock_version of 6.1, but found 99.99. Please file an issue on rules_js", msg) + asserts.equals(env, "npm_translate_lock currently supports a maximum lock_version of 9.0, but found 99.99. Please file an issue on rules_js", msg) return unittest.end(env) # buildifier: disable=function-docstring diff --git a/npm/private/utils.bzl b/npm/private/utils.bzl index 02914c8e9a..7c170884ec 100644 --- a/npm/private/utils.bzl +++ b/npm/private/utils.bzl @@ -160,6 +160,74 @@ def _convert_v6_packages(packages): package_info[key] = dependencies result[_convert_pnpm_v6_package_name(package)] = package_info + + return result + +def _convert_pnpm_v9_package_name(package_name): + # Covert a pnpm lock file v9 name/version@version string of the format + # @scope/name@version(@scope/name@version)(@scope/name@version)@version + # to a pnpm lock file v5 @scope/name/version_peer_version format that is compatible with rules_js. + package_name = _convert_pnpm_v6_version_peer_dep(package_name) + segments = package_name.rsplit("@", 1) + if len(segments) != 2: + msg = "unexpected pnpm versioned name {}".format(package_name) + fail(msg) + return "/%s/%s" % (segments[0], segments[1]) + +# v9 importers are the same as v6 importers +_convert_v9_importers = _convert_v6_importers + +def _convert_v9_packages(packages, snapshots): + # Convert pnpm lockfile v9 importers to a rules_js compatible format. + + # v9 split package metadata (v6 "packages" field) into 2: + # + # packages: + # '@scoped/name@5.0.2' + # hasBin + # resolution (integrity etc) + # peerDependencies which *might* be resolved + # + # snapshots: + # '@scoped/name@2.0.0(peer@2.0.2)' + # dependencies: + # a-dep@1.2.3 + # peer@2.0.2 + # b-dep@3.2.1(peer-b@4.5.6) + # + # Where the 'snapshots' keys contain the peer information while 'packages' contain the static information + # such as hasBin, resolution and peerDependencies that require resolution. + + result = {} + + # Snapshots contains the packages with the keys (which include peers) to return + for package, snapshot_info in snapshots.items(): + # convert v6 package dependencies + optionalDependencies + for key in ["dependencies", "optionalDependencies"]: + deps = snapshot_info.get(key, None) + if deps != None: + dependencies = {} + for dep_name, dep_version in deps.items(): + dependencies[dep_name] = _convert_pnpm_v6_version_peer_dep(dep_version) + snapshot_info[key] = dependencies + + # Strip peer-dep info off to get the raw package + package_version = package + if package_version[-1] == ")": + package_version = package_version[:package_version.find("(")] + + # Metadata for this snapshot persisted in the 'packages' + package_info = packages[package_version] + if package_info == None: + msg = "Failed to find pnpm-lock snapshot %s (%s) in packages" % (package, package_version) + fail(msg) + + # Also include the static data from the 'packages' + for info_name, info_value in package_info.items(): + snapshot_info[info_name] = info_value + + result[_convert_pnpm_v9_package_name(package)] = snapshot_info + return result def _parse_pnpm_lock_yaml(content): @@ -222,7 +290,11 @@ def _parse_pnpm_lock_common(parsed, err): packages = parsed.get("packages", {}) - if lockfile_version >= 6.0: + if lockfile_version >= 9.0: + snapshots = parsed.get("snapshots", {}) + importers = _convert_v9_importers(importers) + packages = _convert_v9_packages(packages, snapshots) + elif lockfile_version >= 6.0: # special handling for lockfile v6 which had breaking changes importers = _convert_v6_importers(importers) packages = _convert_v6_packages(packages) @@ -240,8 +312,9 @@ def _assert_lockfile_version(version, testonly = False): # 5.4 - pnpm v7.0.0 bumped the lockfile version to 5.4 # 6.0 - pnpm v8.0.0 bumped the lockfile version to 6.0; this included breaking changes # 6.1 - pnpm v8.6.0 bumped the lockfile version to 6.1 + # 9.0 - pnpm v9.0.0 bumped the lockfile version to 9.0 min_lock_version = 5.3 - max_lock_version = 6.1 + max_lock_version = 9.0 msg = None if version < min_lock_version: