From ac287c496025cee794bb35ce633113b57605fda6 Mon Sep 17 00:00:00 2001 From: legobt <6wbvkn0j@anonaddy.me> Date: Sat, 26 Oct 2024 00:18:30 +0000 Subject: [PATCH 1/2] feat: allow "npm:name@version" dependency redirections in manifest --- package.json | 2 ++ src/package-manifest.test.ts | 2 ++ src/package-manifest.ts | 38 +++++++++++++++++++++++++++++++----- yarn.lock | 9 +++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4670cad..caf575d 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "execa": "^8.0.1", "pony-cause": "^2.1.9", "semver": "^7.5.4", + "validate-npm-package-name": "^5.0.0", "which": "^3.0.0", "yaml": "^2.2.2", "yargs": "^17.7.1" @@ -52,6 +53,7 @@ "@types/node": "^17.0.23", "@types/prettier": "^2.7.3", "@types/rimraf": "^4.0.5", + "@types/validate-npm-package-name": "^4.0.2", "@types/which": "^3.0.0", "@types/yargs": "^17.0.10", "@typescript-eslint/eslint-plugin": "^5.62.0", diff --git a/src/package-manifest.test.ts b/src/package-manifest.test.ts index af9f77c..e0270b8 100644 --- a/src/package-manifest.test.ts +++ b/src/package-manifest.test.ts @@ -67,6 +67,7 @@ describe('package-manifest', () => { b: '^2.0.0', c: '~4.3.0', d: 'workspace:^', + e: 'npm:a@^2.0.0', }, }; const validated = { @@ -79,6 +80,7 @@ describe('package-manifest', () => { b: '^2.0.0', c: '~4.3.0', d: 'workspace:^', + e: 'npm:a@^2.0.0', }, peerDependencies: {}, }; diff --git a/src/package-manifest.ts b/src/package-manifest.ts index a4aa6d5..94a1613 100644 --- a/src/package-manifest.ts +++ b/src/package-manifest.ts @@ -4,6 +4,7 @@ import { ManifestDependencyFieldNames as PackageManifestDependenciesFieldNames, } from '@metamask/action-utils'; import { isPlainObject } from '@metamask/utils'; +import validateNPMPackageName from 'validate-npm-package-name'; import { readJsonObjectFile } from './fs.js'; import { isTruthyString } from './misc-utils.js'; import { semver, SemVer } from './semver.js'; @@ -144,8 +145,10 @@ function isValidPackageManifestVersionField( /** * Type guard to ensure that the provided version value is a valid dependency version - * specifier for a package manifest. This function validates both semantic versioning - * ranges and the special 'workspace:^' notation. + * specifier for a package manifest. This function validates: + * semantic versioning ranges + * 'workspace:^' notation + * 'npm:{packageName}:{semverRange}' redirections. * * @param version - The value to check. * @returns `true` if the version is a valid string that either @@ -155,9 +158,34 @@ function isValidPackageManifestVersionField( function isValidPackageManifestDependencyValue( version: unknown, ): version is string { - return ( - isValidPackageManifestVersionField(version) || version === 'workspace:^' - ); + if (typeof version !== 'string') { + return false; + } + + if ( + isValidPackageManifestVersionField(version) || + version === 'workspace:^' + ) { + return true; + } + + const redirectedDependencyRegexp = /^npm:(.*)@(.*?)$/u; + + try { + const redirectedDependencyMatch = redirectedDependencyRegexp.exec(version); + + if (!redirectedDependencyMatch || redirectedDependencyMatch.length < 3) { + return false; + } + + const [, redirectedName, redirectedVersion] = redirectedDependencyMatch; + return ( + validateNPMPackageName(redirectedName)?.validForOldPackages && + isValidPackageManifestVersionField(redirectedVersion) + ); + } catch (e) { + return false; + } } /** diff --git a/yarn.lock b/yarn.lock index 5674337..69462c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2090,6 +2090,7 @@ __metadata: "@types/node": ^17.0.23 "@types/prettier": ^2.7.3 "@types/rimraf": ^4.0.5 + "@types/validate-npm-package-name": ^4.0.2 "@types/which": ^3.0.0 "@types/yargs": ^17.0.10 "@typescript-eslint/eslint-plugin": ^5.62.0 @@ -2117,6 +2118,7 @@ __metadata: stdio-mock: ^1.2.0 tsx: ^4.6.1 typescript: ~5.1.6 + validate-npm-package-name: ^5.0.0 which: ^3.0.0 yaml: ^2.2.2 yargs: ^17.7.1 @@ -2606,6 +2608,13 @@ __metadata: languageName: node linkType: hard +"@types/validate-npm-package-name@npm:^4.0.2": + version: 4.0.2 + resolution: "@types/validate-npm-package-name@npm:4.0.2" + checksum: 3f35a3cc8ddd919b456843f36d55a4f1df5f03d5d9b6494b4d8f5f3b24e3f24a11c922772d9970a67f1249214da18c157776e9c6d2e72227799459849dfd9c76 + languageName: node + linkType: hard + "@types/which@npm:^3.0.0": version: 3.0.0 resolution: "@types/which@npm:3.0.0" From 4f5d575acab26ae2538fc563e6fc81a3b98fed1d Mon Sep 17 00:00:00 2001 From: legobt <6wbvkn0j@anonaddy.me> Date: Sat, 26 Oct 2024 00:41:43 +0000 Subject: [PATCH 2/2] chore: test coverage --- src/package-manifest.test.ts | 8 +++++--- src/package-manifest.ts | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/package-manifest.test.ts b/src/package-manifest.test.ts index e0270b8..08c02cc 100644 --- a/src/package-manifest.test.ts +++ b/src/package-manifest.test.ts @@ -67,7 +67,7 @@ describe('package-manifest', () => { b: '^2.0.0', c: '~4.3.0', d: 'workspace:^', - e: 'npm:a@^2.0.0', + e: 'npm:@a/abc@^2.0.0', }, }; const validated = { @@ -80,7 +80,7 @@ describe('package-manifest', () => { b: '^2.0.0', c: '~4.3.0', d: 'workspace:^', - e: 'npm:a@^2.0.0', + e: 'npm:@a/abc@^2.0.0', }, peerDependencies: {}, }; @@ -317,7 +317,9 @@ describe('package-manifest', () => { name: 'foo', version: '1.0.0', peerDependencies: { - a: 12345, + a: 'npm:@foo', + b: 'npm:foo@', + c: '12345', }, }), ); diff --git a/src/package-manifest.ts b/src/package-manifest.ts index 94a1613..eea83e5 100644 --- a/src/package-manifest.ts +++ b/src/package-manifest.ts @@ -174,7 +174,8 @@ function isValidPackageManifestDependencyValue( try { const redirectedDependencyMatch = redirectedDependencyRegexp.exec(version); - if (!redirectedDependencyMatch || redirectedDependencyMatch.length < 3) { + /* istanbul ignore if */ + if (!redirectedDependencyMatch) { return false; } @@ -183,7 +184,7 @@ function isValidPackageManifestDependencyValue( validateNPMPackageName(redirectedName)?.validForOldPackages && isValidPackageManifestVersionField(redirectedVersion) ); - } catch (e) { + } catch (e) /* istanbul ignore next */ { return false; } }