feat: patch package (#4885)

ref #3077
ref #2675
This commit is contained in:
Zoltan Kochan
2022-06-19 11:44:38 +03:00
committed by GitHub
parent 148c028468
commit 2a34b21ced
36 changed files with 274 additions and 41 deletions

View File

@@ -0,0 +1,23 @@
---
"@pnpm/core": minor
"@pnpm/headless": minor
"@pnpm/types": minor
"pnpm": minor
"@pnpm/resolve-dependencies": minor
"@pnpm/lockfile-types": minor
"@pnpm/lifecycle": minor
"@pnpm/plugin-commands-installation": minor
---
Dependencies patching is possible via the `pnpm.patchedDependencies` field of the `package.json`.
To patch a package, the package name, exact version, and the relative path to the patch file should be specified. For instance:
```json
{
"pnpm": {
"patchedDependencies": {
"eslint@1.0.0": "./patches/eslint@1.0.0.patch"
}
}
}
```

View File

@@ -0,0 +1,5 @@
---
"@pnpm/calc-dep-state": major
---
Changed the order of arguments in calcDepState and added an optional last argument for patchFileHash.

View File

@@ -0,0 +1,9 @@
---
"@pnpm/create-cafs-store": major
"@pnpm/fetcher-base": major
"@pnpm/package-store": major
"@pnpm/server": major
"@pnpm/store-controller-types": major
---
Rename engine and targetEngine fields to sideEffectsCacheKey.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/build-modules": minor
---
Support packages patching.

View File

@@ -16,6 +16,10 @@ export interface DependenciesGraphNode {
optionalDependencies: Set<string>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
requiresBuild?: boolean | any // this is a durty workaround added in https://github.com/pnpm/pnpm/pull/4898
patchFile?: {
hash: string
path: string
}
}
export interface DependenciesGraph {

View File

@@ -83,6 +83,7 @@ async function buildDependency (
extraEnv: opts.extraEnv,
initCwd: opts.lockfileDir,
optional: depNode.optional,
patchPath: depNode.patchFile?.path,
pkgRoot: depNode.dir,
rawConfig: opts.rawConfig,
rootModulesDir: opts.rootModulesDir,
@@ -93,8 +94,9 @@ async function buildDependency (
})
if (hasSideEffects && opts.sideEffectsCacheWrite) {
try {
const sideEffectsCacheKey = calcDepState(depGraph, opts.depsStateCache, depPath, depNode.patchFile?.hash)
await opts.storeController.upload(depNode.dir, {
engine: calcDepState(depPath, depGraph, opts.depsStateCache),
sideEffectsCacheKey,
filesIndexFile: depNode.filesIndexFile,
})
} catch (err: any) { // eslint-disable-line

View File

@@ -11,7 +11,7 @@ export interface DepsGraphNode {
}
export interface DepsStateCache {
[nodeId: string]: DepStateObj
[depPath: string]: DepStateObj
}
export interface DepStateObj {
@@ -19,22 +19,27 @@ export interface DepStateObj {
}
export function calcDepState (
nodeId: string,
depsGraph: DepsGraph,
cache: DepsStateCache
cache: DepsStateCache,
depPath: string,
patchFileHash?: string
): string {
const depStateObj = calcDepStateObj(nodeId, depsGraph, cache, new Set())
return `${ENGINE_NAME}-${JSON.stringify(depStateObj)}`
const depStateObj = calcDepStateObj(depPath, depsGraph, cache, new Set())
let result = `${ENGINE_NAME}-${JSON.stringify(depStateObj)}`
if (patchFileHash) {
result += `-${patchFileHash}`
}
return result
}
function calcDepStateObj (
nodeId: string,
depPath: string,
depsGraph: DepsGraph,
cache: DepsStateCache,
parents: Set<string>
): DepStateObj {
if (cache[nodeId]) return cache[nodeId]
const node = depsGraph[nodeId]
if (cache[depPath]) return cache[depPath]
const node = depsGraph[depPath]
if (!node) return {}
const nextParents = new Set([...Array.from(parents), node.depPath])
const state: DepStateObj = {}
@@ -47,6 +52,6 @@ function calcDepStateObj (
}
state[child.depPath] = calcDepStateObj(childId, depsGraph, cache, nextParents)
}
cache[nodeId] = sortKeys(state)
return cache[nodeId]
cache[depPath] = sortKeys(state)
return cache[depPath]
}

View File

@@ -2,7 +2,7 @@ import { calcDepState } from '@pnpm/calc-dep-state'
import { ENGINE_NAME } from '@pnpm/constants'
test('calcDepState()', () => {
expect(calcDepState('/registry/foo/1.0.0', {
expect(calcDepState({
'registry/foo/1.0.0': {
depPath: '/foo/1.0.0',
children: {
@@ -15,5 +15,5 @@ test('calcDepState()', () => {
foo: 'registry/foo/1.0.0',
},
},
}, {})).toBe(`${ENGINE_NAME}-{}`)
}, {}, '/registry/foo/1.0.0')).toBe(`${ENGINE_NAME}-{}`)
})

View File

@@ -100,6 +100,7 @@ export interface StrictInstallOptions {
global: boolean
globalBin?: string
patchedDependencies?: Record<string, string>
}
export type InstallOptions =

View File

@@ -240,6 +240,7 @@ export async function mutateModules (
neverBuiltDependencies: opts.neverBuiltDependencies,
onlyBuiltDependencies: opts.onlyBuiltDependencies,
packageExtensionsChecksum,
patchedDependencies: opts.patchedDependencies,
}) ||
opts.fixLockfile
if (needsFullResolution) {
@@ -247,6 +248,7 @@ export async function mutateModules (
ctx.wantedLockfile.neverBuiltDependencies = opts.neverBuiltDependencies
ctx.wantedLockfile.onlyBuiltDependencies = opts.onlyBuiltDependencies
ctx.wantedLockfile.packageExtensionsChecksum = packageExtensionsChecksum
ctx.wantedLockfile.patchedDependencies = opts.patchedDependencies
}
const frozenLockfile = opts.frozenLockfile ||
opts.frozenLockfileIfExists && ctx.existsWantedLockfile
@@ -490,16 +492,19 @@ function lockfileIsUpToDate (
onlyBuiltDependencies,
overrides,
packageExtensionsChecksum,
patchedDependencies,
}: {
neverBuiltDependencies?: string[]
onlyBuiltDependencies?: string[]
overrides?: Record<string, string>
packageExtensionsChecksum?: string
patchedDependencies?: Record<string, string>
}) {
return !equals(lockfile.overrides ?? {}, overrides ?? {}) ||
!equals((lockfile.neverBuiltDependencies ?? []).sort(), (neverBuiltDependencies ?? []).sort()) ||
!equals(onlyBuiltDependencies?.sort(), lockfile.onlyBuiltDependencies) ||
lockfile.packageExtensionsChecksum !== packageExtensionsChecksum
lockfile.packageExtensionsChecksum !== packageExtensionsChecksum ||
!equals(lockfile.patchedDependencies ?? {}, patchedDependencies ?? {})
}
export function createObjectChecksum (obj: Object) {
@@ -773,6 +778,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
virtualStoreDir: ctx.virtualStoreDir,
wantedLockfile: ctx.wantedLockfile,
workspacePackages: opts.workspacePackages,
patchedDependencies: opts.patchedDependencies,
}
)

View File

@@ -404,9 +404,9 @@ async function linkAllPkgs (
depNodes.map(async (depNode) => {
const filesResponse = await depNode.fetchingFiles()
let targetEngine: string | undefined
let sideEffectsCacheKey: string | undefined
if (opts.sideEffectsCacheRead && filesResponse.sideEffects && !isEmpty(filesResponse.sideEffects)) {
targetEngine = calcDepState(depNode.depPath, opts.depGraph, opts.depsStateCache)
sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.depPath, depNode.patchFile?.hash)
}
if (typeof depNode.requiresBuild === 'function') {
depNode.requiresBuild = await depNode.requiresBuild()
@@ -414,7 +414,7 @@ async function linkAllPkgs (
const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
filesResponse,
force: opts.force,
targetEngine,
sideEffectsCacheKey,
requiresBuild: depNode.requiresBuild,
})
if (importMethod) {

View File

@@ -0,0 +1,12 @@
diff --git a/index.js b/index.js
index 8e020ca..ff3aee4 100644
--- a/index.js
+++ b/index.js
@@ -5,5 +5,6 @@ module.exports = function (n) {
throw new TypeError('Expected a number');
}
+ // patched
return n >= 0;
};

View File

@@ -0,0 +1,90 @@
import fs from 'fs'
import path from 'path'
import { PackageFilesIndex } from '@pnpm/cafs'
import { ENGINE_NAME } from '@pnpm/constants'
import { install } from '@pnpm/core'
import { prepareEmpty } from '@pnpm/prepare'
import fixtures from '@pnpm/test-fixtures'
import rimraf from '@zkochan/rimraf'
import loadJsonFile from 'load-json-file'
import { testDefaults } from '../utils'
const f = fixtures(__dirname)
test('patch package', async () => {
const project = prepareEmpty()
const patchPath = path.join(f.find('patch-pkg'), 'is-positive@1.0.0.patch')
const patchedDependencies = {
'is-positive@1.0.0': path.relative(process.cwd(), patchPath),
}
const opts = await testDefaults({
fastUnpack: false,
sideEffectsCacheRead: true,
sideEffectsCacheWrite: true,
patchedDependencies,
}, {}, {}, { packageImportMethod: 'hardlink' })
await install({
dependencies: {
'is-positive': '1.0.0',
},
}, opts)
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// patched')
const lockfile = await project.readLockfile()
expect(lockfile.patchedDependencies).toStrictEqual(patchedDependencies)
const filesIndexFile = path.join(opts.storeDir, 'files/c7/1ccf199e0fdae37aad13946b937d67bcd35fa111b84d21b3a19439cfdc2812c5d8da8a735e94c2a1ccb77b4583808ee8405313951e7146ac83ede3671dc292-index.json')
const filesIndex = await loadJsonFile<PackageFilesIndex>(filesIndexFile)
const sideEffectsKey = `${ENGINE_NAME}-{}-meyqmf5tej4bwn3gxydpfig6pe`
const patchedFileIntegrity = filesIndex.sideEffects?.[sideEffectsKey]['index.js']?.integrity
expect(patchedFileIntegrity).toBeTruthy()
const originalFileIntegrity = filesIndex.files['index.js'].integrity
expect(originalFileIntegrity).toBeTruthy()
// The integrity of the original file differs from the integrity of the patched file
expect(originalFileIntegrity).not.toEqual(patchedFileIntegrity)
// The same with frozen lockfile
await rimraf('node_modules')
await install({
dependencies: {
'is-positive': '1.0.0',
},
}, {
...opts,
frozenLockfile: true,
})
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// patched')
// The same with frozen lockfile and hoisted node_modules
await rimraf('node_modules')
await install({
dependencies: {
'is-positive': '1.0.0',
},
}, {
...opts,
frozenLockfile: true,
nodeLinker: 'hoisted',
})
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// patched')
process.chdir('..')
fs.mkdirSync('project2')
process.chdir('project2')
await install({
dependencies: {
'is-positive': '1.0.0',
},
}, await testDefaults({
fastUnpack: false,
sideEffectsCacheRead: true,
sideEffectsCacheWrite: true,
offline: true,
}, {}, {}, { packageImportMethod: 'hardlink' }))
// The original file did not break, when a patched version was created
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).not.toContain('// patched')
})

View File

@@ -22,7 +22,7 @@ function createPackageImporter (
const packageImportMethod = opts.packageImportMethod
const gfm = getFlatMap.bind(null, opts.cafsDir)
return async (to, opts) => {
const { filesMap, isBuilt } = gfm(opts.filesResponse, opts.targetEngine)
const { filesMap, isBuilt } = gfm(opts.filesResponse, opts.sideEffectsCacheKey)
const pkgImportMethod = (opts.requiresBuild && !isBuilt)
? 'clone-or-copy'
: (opts.filesResponse.packageImportMethod ?? packageImportMethod)

View File

@@ -23,7 +23,7 @@ export type PackageFilesResponse = {
export interface ImportPackageOpts {
requiresBuild?: boolean
targetEngine?: string
sideEffectsCacheKey?: string
filesResponse: PackageFilesResponse
force: boolean
}

View File

@@ -71,6 +71,7 @@
"@pnpm/calc-dep-state": "workspace:2.0.1",
"@pnpm/constants": "workspace:6.1.0",
"@pnpm/core-loggers": "workspace:7.0.3",
"@pnpm/crypto.base32-hash": "workspace:1.0.0",
"@pnpm/error": "workspace:3.0.1",
"@pnpm/filter-lockfile": "workspace:6.0.6",
"@pnpm/hoist": "workspace:6.1.4",

View File

@@ -679,15 +679,15 @@ async function linkAllPkgs (
throw err
}
let targetEngine: string | undefined
let sideEffectsCacheKey: string | undefined
if (opts.sideEffectsCacheRead && filesResponse.sideEffects && !isEmpty(filesResponse.sideEffects)) {
targetEngine = calcDepState(depNode.dir, opts.depGraph, opts.depsStateCache)
sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.dir, depNode.patchFile?.hash)
}
const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
filesResponse,
force: opts.force,
requiresBuild: depNode.requiresBuild,
targetEngine,
sideEffectsCacheKey,
})
if (importMethod) {
progressLogger.debug({

View File

@@ -87,6 +87,7 @@ async function linkAllPkgsInOrder (
warn: (message: string) => void
}
) {
const _calcDepState = calcDepState.bind(null, graph, opts.depsStateCache)
await Promise.all(
Object.entries(hierarchy).map(async ([dir, deps]) => {
const depNode = graph[dir]
@@ -98,15 +99,15 @@ async function linkAllPkgsInOrder (
throw err
}
let targetEngine: string | undefined
let sideEffectsCacheKey: string | undefined
if (opts.sideEffectsCacheRead && filesResponse.sideEffects && !isEmpty(filesResponse.sideEffects)) {
targetEngine = calcDepState(dir, graph, opts.depsStateCache)
sideEffectsCacheKey = _calcDepState(dir, depNode.patchFile?.hash)
}
const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
filesResponse,
force: opts.force || depNode.depPath !== prevGraph[dir]?.depPath,
requiresBuild: depNode.requiresBuild,
targetEngine,
sideEffectsCacheKey,
})
if (importMethod) {
progressLogger.debug({

View File

@@ -3,6 +3,7 @@ import { WANTED_LOCKFILE } from '@pnpm/constants'
import {
progressLogger,
} from '@pnpm/core-loggers'
import { createBase32HashFromFile } from '@pnpm/crypto.base32-hash'
import {
Lockfile,
PackageSnapshot,
@@ -44,6 +45,10 @@ export interface DependenciesGraphNode {
prepare: boolean
hasBin: boolean
filesIndexFile: string
patchFile?: {
path: string
hash: string
}
}
export interface DependenciesGraph {
@@ -175,6 +180,7 @@ export default async function lockfileToDepGraph (
optionalDependencies: new Set(Object.keys(pkgSnapshot.optionalDependencies ?? {})),
prepare: pkgSnapshot.prepare === true,
requiresBuild: pkgSnapshot.requiresBuild === true,
patchFile: await tryReadPatchFile(opts.lockfileDir, lockfile, pkgName, pkgVersion),
}
pkgSnapshotByLocation[dir] = pkgSnapshot
})
@@ -214,6 +220,18 @@ export default async function lockfileToDepGraph (
return { graph, directDependenciesByImporterId }
}
export async function tryReadPatchFile (lockfileDir: string, lockfile: Lockfile, pkgName: string, pkgVersion: string) {
const patchFileRelativePath = lockfile.patchedDependencies?.[`${pkgName}@${pkgVersion}`]
if (!patchFileRelativePath) {
return undefined
}
const patchFilePath = path.join(lockfileDir, patchFileRelativePath)
return {
path: patchFilePath,
hash: await createBase32HashFromFile(patchFilePath),
}
}
async function getChildrenPaths (
ctx: {
graph: DependenciesGraph

View File

@@ -23,6 +23,7 @@ import {
DepHierarchy,
DirectDependenciesByImporterId,
LockfileToDepGraphResult,
tryReadPatchFile,
} from './lockfileToDepGraph'
export interface LockfileToHoistedDepGraphOptions {
@@ -208,6 +209,7 @@ async function fetchDeps (
optionalDependencies: new Set(Object.keys(pkgSnapshot.optionalDependencies ?? {})),
prepare: pkgSnapshot.prepare === true,
requiresBuild: pkgSnapshot.requiresBuild === true,
patchFile: await tryReadPatchFile(opts.lockfileDir, opts.lockfile, pkgName, pkgVersion),
}
opts.pkgLocationByDepPath[depPath] = dir
depHierarchy[dir] = await fetchDeps(opts, path.join(dir, 'node_modules'), dep.dependencies)

View File

@@ -33,6 +33,9 @@
{
"path": "../core-loggers"
},
{
"path": "../crypto.base32-hash"
},
{
"path": "../dependency-path"
},

View File

@@ -25,6 +25,10 @@ export async function runPostinstallHooks (
if (pkg.scripts == null) {
pkg.scripts = {}
}
if (opts.patchPath) {
pkg.scripts['pnpm:patch'] = `git apply ${opts.patchPath}`
await runLifecycleHook('pnpm:patch', pkg, opts)
}
if (!pkg.scripts.install) {
await checkBindingGyp(opts.pkgRoot, pkg.scripts)
@@ -42,7 +46,8 @@ export async function runPostinstallHooks (
return pkg.scripts.preinstall != null ||
pkg.scripts.install != null ||
pkg.scripts.postinstall != null
pkg.scripts.postinstall != null ||
Boolean(opts.patchPath)
}
/**

View File

@@ -12,6 +12,7 @@ export interface RunLifecycleHookOptions {
extraEnv?: Record<string, string>
initCwd?: string
optional?: boolean
patchPath?: string
pkgRoot: string
rawConfig: object
rootModulesDir: string

View File

@@ -8,6 +8,7 @@ export interface Lockfile {
onlyBuiltDependencies?: string[]
overrides?: Record<string, string>
packageExtensionsChecksum?: string
patchedDependencies?: Record<string, string>
}
export interface ProjectSnapshot {

View File

@@ -53,23 +53,23 @@ export default async function (
upload,
}
async function upload (builtPkgLocation: string, opts: {filesIndexFile: string, engine: string}) {
async function upload (builtPkgLocation: string, opts: {filesIndexFile: string, sideEffectsCacheKey: string}) {
const sideEffectsIndex = await cafs.addFilesFromDir(builtPkgLocation)
// TODO: move this to a function
// This is duplicated in @pnpm/package-requester
const integrity: Record<string, PackageFileInfo> = {}
await Promise.all(
Object.keys(sideEffectsIndex)
.map(async (filename) => {
Object.entries(sideEffectsIndex)
.map(async ([filename, { writeResult, mode, size }]) => {
const {
checkedAt,
integrity: fileIntegrity,
} = await sideEffectsIndex[filename].writeResult
} = await writeResult
integrity[filename] = {
checkedAt,
integrity: fileIntegrity.toString(), // TODO: use the raw Integrity object
mode: sideEffectsIndex[filename].mode,
size: sideEffectsIndex[filename].size,
mode,
size,
}
})
)
@@ -80,7 +80,7 @@ export default async function (
filesIndex = { files: integrity }
}
filesIndex.sideEffects = filesIndex.sideEffects ?? {}
filesIndex.sideEffects[opts.engine] = integrity
filesIndex.sideEffects[opts.sideEffectsCacheKey] = integrity
await writeJsonFile(opts.filesIndexFile, filesIndex, { indent: undefined })
}
}

View File

@@ -11,6 +11,7 @@ export default function getOptionsFromRootManifest (manifest: ProjectManifest):
neverBuiltDependencies?: string[]
onlyBuiltDependencies?: string[]
packageExtensions?: Record<string, PackageExtension>
patchedDependencies?: Record<string, string>
peerDependencyRules?: PeerDependencyRules
} {
// We read Yarn's resolutions field for compatibility
@@ -22,6 +23,7 @@ export default function getOptionsFromRootManifest (manifest: ProjectManifest):
const packageExtensions = manifest.pnpm?.packageExtensions
const peerDependencyRules = manifest.pnpm?.peerDependencyRules
const allowedDeprecatedVersions = manifest.pnpm?.allowedDeprecatedVersions
const patchedDependencies = manifest.pnpm?.patchedDependencies
return {
allowedDeprecatedVersions,
overrides,
@@ -29,5 +31,6 @@ export default function getOptionsFromRootManifest (manifest: ProjectManifest):
onlyBuiltDependencies,
packageExtensions,
peerDependencyRules,
patchedDependencies,
}
}

View File

@@ -31,6 +31,7 @@
"dependencies": {
"@pnpm/constants": "workspace:6.1.0",
"@pnpm/core-loggers": "workspace:7.0.3",
"@pnpm/crypto.base32-hash": "workspace:1.0.0",
"@pnpm/error": "workspace:3.0.1",
"@pnpm/lockfile-types": "workspace:4.0.3",
"@pnpm/lockfile-utils": "workspace:4.0.5",

View File

@@ -256,7 +256,8 @@ async function finishLockfileUpdates (
Boolean(pkgJson.scripts.postinstall)
) ||
filesResponse.filesIndex['binding.gyp'] ||
Object.keys(filesResponse.filesIndex).some((filename) => !(filename.match(/^[.]hooks[\\/]/) == null)) // TODO: optimize this
Object.keys(filesResponse.filesIndex).some((filename) => !(filename.match(/^[.]hooks[\\/]/) == null)) || // TODO: optimize this
depNode.patchFile != null
)
} else {
// This should never ever happen

View File

@@ -4,6 +4,7 @@ import {
progressLogger,
skippedOptionalDependencyLogger,
} from '@pnpm/core-loggers'
import { createBase32HashFromFile } from '@pnpm/crypto.base32-hash'
import PnpmError from '@pnpm/error'
import {
Lockfile,
@@ -129,6 +130,7 @@ export interface ResolutionContext {
resolvedPackagesByDepPath: ResolvedPackagesByDepPath
outdatedDependencies: {[pkgId: string]: string}
childrenByParentDepPath: ChildrenByParentDepPath
patchedDependencies?: Record<string, string>
pendingNodes: PendingNode[]
wantedLockfile: Lockfile
currentLockfile: Lockfile
@@ -195,6 +197,10 @@ export interface ResolvedPackage {
optionalDependencies: Set<string>
hasBin: boolean
hasBundledDependencies: boolean
patchFile?: {
path: string
hash: string
}
prepare: boolean
depPath: string
requiresBuild: boolean | SafePromiseDefer<boolean>
@@ -915,12 +921,18 @@ async function resolveDependency (
status: 'resolved',
})
ctx.resolvedPackagesByDepPath[depPath] = getResolvedPackage({
let patchPath = ctx.patchedDependencies?.[`${pkg.name}@${pkg.version}`]
if (patchPath) {
patchPath = path.join(ctx.lockfileDir, patchPath)
}
ctx.resolvedPackagesByDepPath[depPath] = await getResolvedPackage({
allowBuild: ctx.allowBuild,
dependencyLockfile: currentPkg.dependencyLockfile,
depPath,
force: ctx.force,
hasBin,
patchPath,
pkg,
pkgResponse,
prepare,
@@ -1005,19 +1017,20 @@ function pkgIsLeaf (pkg: PackageManifest) {
isEmpty(pkg.peerDependencies ?? {})
}
function getResolvedPackage (
async function getResolvedPackage (
options: {
allowBuild?: (pkgName: string) => boolean
dependencyLockfile?: PackageSnapshot
depPath: string
force: boolean
hasBin: boolean
patchPath?: string
pkg: PackageManifest
pkgResponse: PackageResponse
prepare: boolean
wantedDependency: WantedDependency
}
) {
): Promise<ResolvedPackage> {
const peerDependencies = peerDependenciesWithoutOwn(options.pkg)
const requiresBuild = (options.allowBuild == null || options.allowBuild(options.pkg.name))
@@ -1046,6 +1059,9 @@ function getResolvedPackage (
name: options.pkg.name,
optional: options.wantedDependency.optional,
optionalDependencies: new Set(Object.keys(options.pkg.optionalDependencies ?? {})),
patchFile: options.patchPath
? { path: options.patchPath, hash: await createBase32HashFromFile(options.patchPath) }
: undefined,
peerDependencies: peerDependencies ?? {},
peerDependenciesMeta: options.pkg.peerDependenciesMeta,
prepare: options.prepare,

View File

@@ -67,6 +67,7 @@ export interface ResolveDependenciesOptions {
}
nodeVersion: string
registries: Registries
patchedDependencies?: Record<string, string>
pnpmVersion: string
preferredVersions?: PreferredVersions
preferWorkspacePackages?: boolean
@@ -103,6 +104,7 @@ export default async function<T> (
lockfileDir: opts.lockfileDir,
nodeVersion: opts.nodeVersion,
outdatedDependencies: {} as {[pkgId: string]: string},
patchedDependencies: opts.patchedDependencies,
pendingNodes: [] as PendingNode[],
pnpmVersion: opts.pnpmVersion,
preferWorkspacePackages: opts.preferWorkspacePackages,

View File

@@ -15,6 +15,9 @@
{
"path": "../core-loggers"
},
{
"path": "../crypto.base32-hash"
},
{
"path": "../dependency-path"
},

View File

@@ -46,7 +46,7 @@ export default async function (
stop: async () => {
await limitedFetch(`${remotePrefix}/stop`, {})
},
upload: async (builtPkgLocation: string, opts: {filesIndexFile: string, engine: string}) => {
upload: async (builtPkgLocation: string, opts: {filesIndexFile: string, sideEffectsCacheKey: string}) => {
await limitedFetch(`${remotePrefix}/upload`, {
builtPkgLocation,
opts,

View File

@@ -167,7 +167,7 @@ test('server upload', async () => {
const filesIndexFile = path.join(storeDir, 'test.example.com/fake-pkg/1.0.0.json')
await storeCtrl.upload(path.join(__dirname, 'side-effect-fake-dir'), {
engine: fakeEngine,
sideEffectsCacheKey: fakeEngine,
filesIndexFile,
})
@@ -199,7 +199,7 @@ test('disable server upload', async () => {
let thrown = false
try {
await storeCtrl.upload(path.join(__dirname, 'side-effect-fake-dir'), {
engine: fakeEngine,
sideEffectsCacheKey: fakeEngine,
filesIndexFile,
})
} catch (e) {

View File

@@ -35,13 +35,20 @@ DependencyManifest,
| 'version'
>
export interface UploadPkgToStoreOpts {
filesIndexFile: string
sideEffectsCacheKey: string
}
export type UploadPkgToStore = (builtPkgLocation: string, opts: UploadPkgToStoreOpts) => Promise<void>
export interface StoreController {
requestPackage: RequestPackageFunction
fetchPackage: FetchPackageToStoreFunction
importPackage: ImportPackageFunction
close: () => Promise<void>
prune: () => Promise<void>
upload: (builtPkgLocation: string, opts: {filesIndexFile: string, engine: string}) => Promise<void>
upload: UploadPkgToStore
}
export type FetchPackageToStoreFunction = (

View File

@@ -50,6 +50,7 @@ export interface DependenciesMeta {
[dependencyName: string]: {
injected?: boolean
node?: string
patch?: string
}
}
@@ -126,6 +127,7 @@ export type ProjectManifest = BaseManifest & {
packageExtensions?: Record<string, PackageExtension>
peerDependencyRules?: PeerDependencyRules
allowedDeprecatedVersions?: AllowedDeprecatedVersions
patchedDependencies?: Record<string, string>
}
private?: boolean
resolutions?: Record<string, string>

4
pnpm-lock.yaml generated
View File

@@ -1125,6 +1125,7 @@ importers:
'@pnpm/client': workspace:7.1.5
'@pnpm/constants': workspace:6.1.0
'@pnpm/core-loggers': workspace:7.0.3
'@pnpm/crypto.base32-hash': workspace:1.0.0
'@pnpm/error': workspace:3.0.1
'@pnpm/filter-lockfile': workspace:6.0.6
'@pnpm/headless': workspace:18.2.0
@@ -1173,6 +1174,7 @@ importers:
'@pnpm/calc-dep-state': link:../calc-dep-state
'@pnpm/constants': link:../constants
'@pnpm/core-loggers': link:../core-loggers
'@pnpm/crypto.base32-hash': link:../crypto.base32-hash
'@pnpm/error': link:../error
'@pnpm/filter-lockfile': link:../filter-lockfile
'@pnpm/hoist': link:../hoist
@@ -3211,6 +3213,7 @@ importers:
specifiers:
'@pnpm/constants': workspace:6.1.0
'@pnpm/core-loggers': workspace:7.0.3
'@pnpm/crypto.base32-hash': workspace:1.0.0
'@pnpm/error': workspace:3.0.1
'@pnpm/lockfile-types': workspace:4.0.3
'@pnpm/lockfile-utils': workspace:4.0.5
@@ -3246,6 +3249,7 @@ importers:
dependencies:
'@pnpm/constants': link:../constants
'@pnpm/core-loggers': link:../core-loggers
'@pnpm/crypto.base32-hash': link:../crypto.base32-hash
'@pnpm/error': link:../error
'@pnpm/lockfile-types': link:../lockfile-types
'@pnpm/lockfile-utils': link:../lockfile-utils