Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions config/config/src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Catalogs } from '@pnpm/catalogs.types'
import {
type Project,
type ProjectManifest,
type DevEngineDependency,
type ProjectsGraph,
type Registries,
type SslConfig,
Expand All @@ -11,11 +12,6 @@ import { type OptionsFromRootManifest } from './getOptionsFromRootManifest'

export type UniversalOptions = Pick<Config, 'color' | 'dir' | 'rawConfig' | 'rawLocalConfig'>

export interface WantedPackageManager {
name: string
version?: string
}

export type VerifyDepsBeforeRun = 'install' | 'warn' | 'error' | 'prompt' | false

export interface Config extends OptionsFromRootManifest {
Expand Down Expand Up @@ -85,7 +81,7 @@ export interface Config extends OptionsFromRootManifest {
name: string
version: string
}
wantedPackageManager?: WantedPackageManager
wantedPackageManager?: DevEngineDependency
preferOffline?: boolean
sideEffectsCache?: boolean // for backward compatibility
sideEffectsCacheReadonly?: boolean // for backward compatibility
Expand Down
3 changes: 1 addition & 2 deletions config/config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
type ConfigWithDeprecatedSettings,
type UniversalOptions,
type VerifyDepsBeforeRun,
type WantedPackageManager,
} from './Config'
import { getDefaultWorkspaceConcurrency, getWorkspaceConcurrency } from './concurrency'
import { readWorkspaceManifest } from '@pnpm/workspace.read-manifest'
Expand All @@ -42,7 +41,7 @@ export { getOptionsFromRootManifest, getOptionsFromPnpmSettings, type OptionsFro
export * from './readLocalConfig'
export { getDefaultWorkspaceConcurrency, getWorkspaceConcurrency } from './concurrency'

export type { Config, UniversalOptions, WantedPackageManager, VerifyDepsBeforeRun }
export type { Config, UniversalOptions, VerifyDepsBeforeRun }

type CamelToKebabCase<S extends string> = S extends `${infer T}${infer U}`
? `${T extends Capitalize<T> ? '-' : ''}${Lowercase<T>}${CamelToKebabCase<U>}`
Expand Down
14 changes: 14 additions & 0 deletions packages/types/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ export interface DependenciesMeta {
}
}

export interface DevEngineDependency {
name: string
version?: string
onFail?: 'ignore' | 'warn' | 'error' | 'download'
}
export interface DevEngines {
os?: DevEngineDependency | DevEngineDependency[]
cpu?: DevEngineDependency | DevEngineDependency[]
libc?: DevEngineDependency | DevEngineDependency[]
runtime?: DevEngineDependency | DevEngineDependency[]
packageManager?: DevEngineDependency | DevEngineDependency[]
}

export interface PublishConfig extends Record<string, unknown> {
directory?: string
linkDirectory?: boolean
Expand Down Expand Up @@ -96,6 +109,7 @@ export interface BaseManifest {
}
scripts?: PackageScripts
config?: object
devEngines?: DevEngines
engines?: {
node?: string
npm?: string
Expand Down
59 changes: 53 additions & 6 deletions pnpm/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ if (!global['pnpm__startedAt']) {
import loudRejection from 'loud-rejection'
import { packageManager, isExecutedByCorepack } from '@pnpm/cli-meta'
import { getConfig } from '@pnpm/cli-utils'
import { type Config, type WantedPackageManager } from '@pnpm/config'
import { type Config } from '@pnpm/config'
import { type DevEngineDependency } from '@pnpm/types'
import { executionTimeLogger, scopeLogger } from '@pnpm/core-loggers'
import { PnpmError } from '@pnpm/error'
import { filterPackagesFromDir } from '@pnpm/filter-workspace-packages'
import { globalWarn, logger } from '@pnpm/logger'
import { type ParsedCliArgs } from '@pnpm/parse-cli-args'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { finishWorkers } from '@pnpm/worker'
import os from 'os'
import semver from 'semver'
import chalk from 'chalk'
import path from 'path'
import isEmpty from 'ramda/src/isEmpty'
Expand Down Expand Up @@ -283,6 +286,50 @@ export async function main (inputArgv: string[]): Promise<void> {
...(workspaceDir ? { workspacePrefix: workspaceDir } : {}),
})

const {devEngines} = config.rootProjectManifest ?? {}
if (devEngines?.os) {
[devEngines.os].flat().forEach(({ name, version, onFail }) => {
const platforms = [name].flat()
if (
platforms.every(os => os !== process.platform && !os.startsWith('!')) ||
platforms.some(os => os === `!${process.platform}`)
) {
const msg = `This project is configured not to run on ${process.platform}`

if (onFail === 'error' || onFail == null) {
throw new PnpmError('OTHER_OS_EXPECTED', msg)
}
if (onFail === 'warn') globalWarn(msg)
}
const release = os.release()
if (version && !semver.satisfies(release, version)) {
const msg = `This project is configured to use version "${version}" of ${process.platform}. Your current ${process.platform} is v${release}`

if (onFail === 'error' || onFail == null) {
throw new PnpmError('BAD_OS_VERSION', msg)
}
if (onFail === 'warn') globalWarn(msg)
}
})
}
if (devEngines?.cpu) {
[devEngines.cpu].flat().forEach(({ name, onFail }) => {
const architectures = [name].flat()
if (
architectures.every(cpu => cpu !== process.arch && !cpu.startsWith('!')) ||
architectures.some(cpu => cpu === `!${process.arch}`)
) {
const msg = `This project is configured not to run on ${process.arch} architecture`

if (onFail === 'error' || onFail == null) {
throw new PnpmError('OTHER_CPU_EXPECTED', msg)
}
if (onFail === 'warn') globalWarn(msg)
}
})
}
[devEngines?.packageManager].flat().forEach(pm => pm && checkPackageManager(pm, config))

if (config.useNodeVersion != null) {
if ('webcontainer' in process.versions) {
globalWarn('Automatic installation of different Node.js versions is not supported in WebContainer')
Expand Down Expand Up @@ -347,25 +394,25 @@ function printError (message: string, hint?: string): void {
}
}

function checkPackageManager (pm: WantedPackageManager, config: Config): void {
function checkPackageManager (pm: DevEngineDependency, config: Config): void {
if (!pm.name) return
if (pm.name !== 'pnpm') {
const msg = `This project is configured to use ${pm.name}`
if (config.packageManagerStrict) {
if (config.packageManagerStrict || pm.onFail === 'error' || pm.onFail == null) {
throw new PnpmError('OTHER_PM_EXPECTED', msg)
}
globalWarn(msg)
if (pm.onFail === 'warn') globalWarn(msg)
} else {
const currentPnpmVersion = packageManager.name === 'pnpm'
? packageManager.version
: undefined
if (currentPnpmVersion && config.packageManagerStrictVersion && pm.version && pm.version !== currentPnpmVersion) {
const msg = `This project is configured to use v${pm.version} of pnpm. Your current pnpm is v${currentPnpmVersion}`
if (config.packageManagerStrict) {
if (config.packageManagerStrict || pm.onFail === 'error' || pm.onFail == null) {
throw new PnpmError('BAD_PM_VERSION', msg, {
hint: 'If you want to bypass this version check, you can set the "package-manager-strict" configuration to "false" or set the "COREPACK_ENABLE_STRICT" environment variable to "0"',
})
} else {
} else if (pm.onFail === 'warn') {
globalWarn(msg)
}
}
Expand Down
Loading