@pnpm/config.package-is-installable 1100.0.9 → 1100.0.10

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.
package/lib/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { UnsupportedEngineError, type WantedEngine } from './checkEngine.js';
3
3
  import { UnsupportedPlatformError } from './checkPlatform.js';
4
4
  export type { Engine } from './checkEngine.js';
5
5
  export type { Platform, WantedPlatform } from './checkPlatform.js';
6
+ export { inferPlatformFromPackageName } from './inferPlatformFromPackageName.js';
6
7
  export { UnsupportedEngineError, UnsupportedPlatformError, type WantedEngine, };
7
8
  export declare function packageIsInstallable(pkgId: string, pkg: {
8
9
  name: string;
package/lib/index.js CHANGED
@@ -2,9 +2,11 @@ import { installCheckLogger, skippedOptionalDependencyLogger, } from '@pnpm/core
2
2
  import { getSystemNodeVersion } from '@pnpm/engine.runtime.system-version';
3
3
  import { checkEngine, UnsupportedEngineError } from './checkEngine.js';
4
4
  import { checkPlatform, UnsupportedPlatformError } from './checkPlatform.js';
5
+ import { inferPlatformFromPackageName } from './inferPlatformFromPackageName.js';
6
+ export { inferPlatformFromPackageName } from './inferPlatformFromPackageName.js';
5
7
  export { UnsupportedEngineError, UnsupportedPlatformError, };
6
8
  export function packageIsInstallable(pkgId, pkg, options) {
7
- const warn = checkPackage(pkgId, pkg, options);
9
+ const warn = checkPackage(pkgId, { engines: pkg.engines, ...effectivePlatform(pkg, options.optional) }, options);
8
10
  if (warn == null)
9
11
  return true;
10
12
  installCheckLogger.warn({
@@ -28,6 +30,32 @@ export function packageIsInstallable(pkgId, pkg, options) {
28
30
  throw warn;
29
31
  return null;
30
32
  }
33
+ /**
34
+ * The platform fields of an optional dependency may be incomplete: some
35
+ * registries strip os/cpu/libc (or just libc) from the metadata they serve,
36
+ * and lockfile entries written from such metadata lack them too. For a
37
+ * platform-specific binary the package name carries the same information, so
38
+ * each missing field is filled from the name's tokens. A package that
39
+ * declares no platform fields at all is treated as platform-specific only
40
+ * when an operating system is recognized in its name — a generic name
41
+ * segment (e.g. `arm` on its own) never marks it as such.
42
+ * https://github.com/pnpm/pnpm/issues/11702
43
+ */
44
+ function effectivePlatform(pkg, optional) {
45
+ if (!optional || (pkg.os != null && pkg.cpu != null && pkg.libc != null))
46
+ return pkg;
47
+ const inferred = inferPlatformFromPackageName(pkg.name);
48
+ if (inferred == null)
49
+ return pkg;
50
+ const pkgDeclaresPlatform = pkg.os != null || pkg.cpu != null || pkg.libc != null;
51
+ if (!pkgDeclaresPlatform && inferred.os == null)
52
+ return pkg;
53
+ return {
54
+ os: pkg.os ?? inferred.os,
55
+ cpu: pkg.cpu ?? inferred.cpu,
56
+ libc: pkg.libc ?? inferred.libc,
57
+ };
58
+ }
31
59
  export function checkPackage(pkgId, manifest, options) {
32
60
  return checkPlatform(pkgId, {
33
61
  cpu: manifest.cpu ?? ['any'],
@@ -0,0 +1,13 @@
1
+ export interface PlatformInferredFromPackageName {
2
+ os?: string[];
3
+ cpu?: string[];
4
+ libc?: string[];
5
+ }
6
+ /**
7
+ * Infers the supported platforms of a package from the tokens of its name,
8
+ * e.g. `@nx/nx-win32-arm64-msvc` → `{ os: ['win32'], cpu: ['arm64'] }`.
9
+ * Platform-specific binary packages follow this naming convention, which is
10
+ * the only platform signal left when their os/cpu/libc manifest fields are
11
+ * absent. Returns null when no platform token is recognized in the name.
12
+ */
13
+ export declare function inferPlatformFromPackageName(name: string): PlatformInferredFromPackageName | null;
@@ -0,0 +1,71 @@
1
+ const OS_BY_TOKEN = new Map([
2
+ ['aix', 'aix'],
3
+ ['android', 'android'],
4
+ ['darwin', 'darwin'],
5
+ ['macos', 'darwin'],
6
+ ['osx', 'darwin'],
7
+ ['freebsd', 'freebsd'],
8
+ ['linux', 'linux'],
9
+ ['netbsd', 'netbsd'],
10
+ ['openbsd', 'openbsd'],
11
+ ['openharmony', 'openharmony'],
12
+ ['sunos', 'sunos'],
13
+ ['win32', 'win32'],
14
+ ['windows', 'win32'],
15
+ ]);
16
+ const CPU_BY_TOKEN = new Map([
17
+ ['arm', 'arm'],
18
+ ['armv6', 'arm'],
19
+ ['armv7', 'arm'],
20
+ ['arm64', 'arm64'],
21
+ ['aarch64', 'arm64'],
22
+ ['ia32', 'ia32'],
23
+ ['loong64', 'loong64'],
24
+ ['mips64el', 'mips64el'],
25
+ ['ppc64', 'ppc64'],
26
+ ['ppc64le', 'ppc64'],
27
+ ['riscv64', 'riscv64'],
28
+ ['s390x', 's390x'],
29
+ ['x64', 'x64'],
30
+ ['amd64', 'x64'],
31
+ ['wasm32', 'wasm32'],
32
+ ]);
33
+ const LIBC_BY_TOKEN = new Map([
34
+ ['glibc', 'glibc'],
35
+ ['gnu', 'glibc'],
36
+ ['gnueabihf', 'glibc'],
37
+ ['musl', 'musl'],
38
+ ['musleabihf', 'musl'],
39
+ ]);
40
+ /**
41
+ * Infers the supported platforms of a package from the tokens of its name,
42
+ * e.g. `@nx/nx-win32-arm64-msvc` → `{ os: ['win32'], cpu: ['arm64'] }`.
43
+ * Platform-specific binary packages follow this naming convention, which is
44
+ * the only platform signal left when their os/cpu/libc manifest fields are
45
+ * absent. Returns null when no platform token is recognized in the name.
46
+ */
47
+ export function inferPlatformFromPackageName(name) {
48
+ const nameWithoutScope = name.includes('/') ? name.slice(name.indexOf('/') + 1) : name;
49
+ const tokens = nameWithoutScope.toLowerCase().split(/[-_.]/);
50
+ const os = pickTokenValues(tokens, OS_BY_TOKEN);
51
+ const cpu = pickTokenValues(tokens, CPU_BY_TOKEN);
52
+ const libc = pickTokenValues(tokens, LIBC_BY_TOKEN);
53
+ if (os == null && cpu == null && libc == null)
54
+ return null;
55
+ return {
56
+ ...(os != null ? { os } : {}),
57
+ ...(cpu != null ? { cpu } : {}),
58
+ ...(libc != null ? { libc } : {}),
59
+ };
60
+ }
61
+ function pickTokenValues(tokens, valueByToken) {
62
+ const values = new Set();
63
+ for (const token of tokens) {
64
+ const value = valueByToken.get(token);
65
+ if (value != null) {
66
+ values.add(value);
67
+ }
68
+ }
69
+ return values.size > 0 ? Array.from(values) : undefined;
70
+ }
71
+ //# sourceMappingURL=inferPlatformFromPackageName.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/config.package-is-installable",
3
- "version": "1100.0.9",
3
+ "version": "1100.0.10",
4
4
  "description": "Checks if a package is installable on the current system",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -31,11 +31,11 @@
31
31
  "execa": "npm:safe-execa@0.3.0",
32
32
  "memoize": "^10.2.0",
33
33
  "semver": "^7.8.1",
34
- "@pnpm/core-loggers": "1100.1.4",
35
- "@pnpm/error": "1100.0.0",
36
- "@pnpm/types": "1101.3.1",
37
34
  "@pnpm/cli.meta": "1100.0.7",
38
- "@pnpm/engine.runtime.system-version": "1100.0.2"
35
+ "@pnpm/core-loggers": "1100.2.0",
36
+ "@pnpm/engine.runtime.system-version": "1100.0.2",
37
+ "@pnpm/types": "1101.3.1",
38
+ "@pnpm/error": "1100.0.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@pnpm/logger": "^1001.0.1"
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@jest/globals": "30.3.0",
45
45
  "@types/semver": "7.7.1",
46
- "@pnpm/config.package-is-installable": "1100.0.9",
46
+ "@pnpm/config.package-is-installable": "1100.0.10",
47
47
  "@pnpm/logger": "1100.0.0"
48
48
  },
49
49
  "engines": {