mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
feat: add lockfile-include-tarball-url option (#5054)
* feat: add save-tarball-url .npmrc option - add save-tarball-url option, which saves resolved tarball URL to pnpm-lock.yaml during install or add command * feat: add save-tarball-url .npmrc option - remove incorrect change - remove help section for new option * feat: add lockfile-include-tarball-url option - change option name, add test - option is now named lockfile-include-tarball-url as suggested by @zkochan - add test covering new feature * feat: add lockfile-include-tarball-url option - add changeset - add changeset * refactor: lockfile-include-tarball-url Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
8
.changeset/proud-items-fold.md
Normal file
8
.changeset/proud-items-fold.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@pnpm/config": patch
|
||||
"@pnpm/core": patch
|
||||
"@pnpm/plugin-commands-installation": patch
|
||||
"@pnpm/resolve-dependencies": patch
|
||||
---
|
||||
|
||||
When `lockfile-include-tarball-url` is set to `true`, every entry in `pnpm-lock.yaml` will contain the full URL to the package's tarball [#5054](https://github.com/pnpm/pnpm/pull/5054).
|
||||
@@ -35,6 +35,7 @@ export interface Config {
|
||||
saveOptional?: boolean
|
||||
savePeer?: boolean
|
||||
saveWorkspaceProtocol?: boolean | 'rolling'
|
||||
lockfileIncludeTarballUrl?: boolean
|
||||
scriptShell?: string
|
||||
stream?: boolean
|
||||
pnpmExecPath: string
|
||||
|
||||
@@ -69,6 +69,7 @@ export const types = Object.assign({
|
||||
lockfile: Boolean,
|
||||
'lockfile-dir': String,
|
||||
'lockfile-directory': String, // TODO: deprecate
|
||||
'lockfile-include-tarball-url': Boolean,
|
||||
'lockfile-only': Boolean,
|
||||
loglevel: ['silent', 'error', 'warn', 'info', 'debug'],
|
||||
maxsockets: Number,
|
||||
@@ -197,6 +198,7 @@ export default async (
|
||||
'hoist-pattern': ['*'],
|
||||
'ignore-workspace-root-check': false,
|
||||
'link-workspace-packages': true,
|
||||
'lockfile-include-tarball-url': false,
|
||||
'modules-cache-max-age': 7 * 24 * 60, // 7 days
|
||||
'node-linker': 'isolated',
|
||||
'package-lock': npmDefaults['package-lock'],
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface StrictInstallOptions {
|
||||
ignorePackageManifest: boolean
|
||||
preferFrozenLockfile: boolean
|
||||
saveWorkspaceProtocol: boolean | 'rolling'
|
||||
lockfileIncludeTarballUrl: boolean
|
||||
preferWorkspacePackages: boolean
|
||||
preserveWorkspaceProtocol: boolean
|
||||
scriptsPrependNodePath: boolean | 'warn-only'
|
||||
@@ -155,6 +156,7 @@ const defaults = async (opts: InstallOptions) => {
|
||||
rawConfig: {},
|
||||
registries: DEFAULT_REGISTRIES,
|
||||
saveWorkspaceProtocol: true,
|
||||
lockfileIncludeTarballUrl: false,
|
||||
scriptsPrependNodePath: false,
|
||||
shamefullyHoist: false,
|
||||
shellEmulator: false,
|
||||
|
||||
@@ -810,6 +810,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
wantedLockfile: ctx.wantedLockfile,
|
||||
workspacePackages: opts.workspacePackages,
|
||||
patchedDependencies: opts.patchedDependencies,
|
||||
lockfileIncludeTarballUrl: opts.lockfileIncludeTarballUrl,
|
||||
}
|
||||
)
|
||||
if (!opts.include.optionalDependencies || !opts.include.devDependencies || !opts.include.dependencies) {
|
||||
|
||||
@@ -1376,3 +1376,14 @@ test('a broken lockfile should not break the store', async () => {
|
||||
},
|
||||
], await testDefaults({ lockfileOnly: true, storeDir: path.resolve('store2') }))
|
||||
})
|
||||
|
||||
test('include tarball URL', async () => {
|
||||
const project = prepareEmpty()
|
||||
|
||||
const opts = await testDefaults({ fastUnpack: false, lockfileIncludeTarballUrl: true })
|
||||
await addDependenciesToPackage({}, ['pkg-with-1-dep@100.0.0'], opts)
|
||||
|
||||
const lockfile = await project.readLockfile()
|
||||
expect((lockfile.packages['/pkg-with-1-dep/100.0.0'].resolution as TarballResolution).tarball)
|
||||
.toBe(`http://localhost:${REGISTRY_MOCK_PORT}/pkg-with-1-dep/-/pkg-with-1-dep-100.0.0.tgz`)
|
||||
})
|
||||
|
||||
@@ -268,6 +268,7 @@ export type InstallCommandOptions = Pick<Config,
|
||||
| 'savePrefix'
|
||||
| 'saveProd'
|
||||
| 'saveWorkspaceProtocol'
|
||||
| 'lockfileIncludeTarballUrl'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'sideEffectsCache'
|
||||
| 'sideEffectsCacheReadonly'
|
||||
|
||||
@@ -62,6 +62,7 @@ export type InstallDepsOptions = Pick<Config,
|
||||
| 'savePrefix'
|
||||
| 'saveProd'
|
||||
| 'saveWorkspaceProtocol'
|
||||
| 'lockfileIncludeTarballUrl'
|
||||
| 'scriptsPrependNodePath'
|
||||
| 'scriptShell'
|
||||
| 'selectedProjectsGraph'
|
||||
|
||||
@@ -64,6 +64,7 @@ type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
|
||||
| 'savePrefix'
|
||||
| 'saveProd'
|
||||
| 'saveWorkspaceProtocol'
|
||||
| 'lockfileIncludeTarballUrl'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
| 'tag'
|
||||
> & {
|
||||
|
||||
@@ -40,6 +40,7 @@ export function rcOptionsTypes () {
|
||||
'lockfile-directory',
|
||||
'lockfile-only',
|
||||
'lockfile',
|
||||
'lockfile-include-tarball-url',
|
||||
'network-concurrency',
|
||||
'noproxy',
|
||||
'npmPath',
|
||||
|
||||
@@ -82,6 +82,7 @@ export default async function (
|
||||
defaultUpdateDepth: number
|
||||
preserveWorkspaceProtocol: boolean
|
||||
saveWorkspaceProtocol: 'rolling' | boolean
|
||||
lockfileIncludeTarballUrl?: boolean
|
||||
}
|
||||
) {
|
||||
const _toResolveImporter = toResolveImporter.bind(null, {
|
||||
@@ -212,7 +213,13 @@ export default async function (
|
||||
}
|
||||
}
|
||||
|
||||
const { newLockfile, pendingRequiresBuilds } = updateLockfile(dependenciesGraph, opts.wantedLockfile, opts.virtualStoreDir, opts.registries) // eslint-disable-line:prefer-const
|
||||
const { newLockfile, pendingRequiresBuilds } = updateLockfile({
|
||||
dependenciesGraph,
|
||||
lockfile: opts.wantedLockfile,
|
||||
prefix: opts.virtualStoreDir,
|
||||
registries: opts.registries,
|
||||
lockfileIncludeTarballUrl: opts.lockfileIncludeTarballUrl,
|
||||
})
|
||||
|
||||
if (opts.forceFullResolution && opts.wantedLockfile != null) {
|
||||
for (const [depPath, pkg] of Object.entries(dependenciesGraph)) {
|
||||
|
||||
@@ -20,30 +20,33 @@ import { ResolvedPackage } from './resolveDependencies'
|
||||
import { DependenciesGraph } from '.'
|
||||
|
||||
export default function (
|
||||
depGraph: DependenciesGraph,
|
||||
lockfile: Lockfile,
|
||||
prefix: string,
|
||||
registries: Registries
|
||||
{ dependenciesGraph, lockfile, prefix, registries, lockfileIncludeTarballUrl }: {
|
||||
dependenciesGraph: DependenciesGraph
|
||||
lockfile: Lockfile
|
||||
prefix: string
|
||||
registries: Registries
|
||||
lockfileIncludeTarballUrl?: boolean
|
||||
}
|
||||
): {
|
||||
newLockfile: Lockfile
|
||||
pendingRequiresBuilds: string[]
|
||||
} {
|
||||
lockfile.packages = lockfile.packages ?? {}
|
||||
const pendingRequiresBuilds = [] as string[]
|
||||
for (const depPath of Object.keys(depGraph)) {
|
||||
const depNode = depGraph[depPath]
|
||||
for (const [depPath, depNode] of Object.entries(dependenciesGraph)) {
|
||||
const [updatedOptionalDeps, updatedDeps] = partition(
|
||||
(child) => depNode.optionalDependencies.has(child.alias),
|
||||
Object.keys(depNode.children).map((alias) => ({ alias, depPath: depNode.children[alias] }))
|
||||
)
|
||||
lockfile.packages[depPath] = toLockfileDependency(pendingRequiresBuilds, depNode, {
|
||||
depGraph,
|
||||
depGraph: dependenciesGraph,
|
||||
depPath,
|
||||
prevSnapshot: lockfile.packages[depPath],
|
||||
registries,
|
||||
registry: dp.getRegistryByPackageName(registries, depNode.name),
|
||||
updatedDeps,
|
||||
updatedOptionalDeps,
|
||||
lockfileIncludeTarballUrl,
|
||||
})
|
||||
}
|
||||
const warn = (message: string) => logger.warn({ message, prefix })
|
||||
@@ -64,13 +67,15 @@ function toLockfileDependency (
|
||||
updatedOptionalDeps: Array<{alias: string, depPath: string}>
|
||||
depGraph: DependenciesGraph
|
||||
prevSnapshot?: PackageSnapshot
|
||||
lockfileIncludeTarballUrl?: boolean
|
||||
}
|
||||
): PackageSnapshot {
|
||||
const lockfileResolution = toLockfileResolution(
|
||||
{ id: pkg.id, name: pkg.name, version: pkg.version },
|
||||
opts.depPath,
|
||||
pkg.resolution,
|
||||
opts.registry
|
||||
opts.registry,
|
||||
opts.lockfileIncludeTarballUrl
|
||||
)
|
||||
const newResolvedDeps = updateResolvedDeps(
|
||||
opts.prevSnapshot?.dependencies ?? {},
|
||||
@@ -227,13 +232,21 @@ function toLockfileResolution (
|
||||
},
|
||||
depPath: string,
|
||||
resolution: Resolution,
|
||||
registry: string
|
||||
registry: string,
|
||||
lockfileIncludeTarballUrl?: boolean
|
||||
): LockfileResolution {
|
||||
/* eslint-disable @typescript-eslint/dot-notation */
|
||||
if (dp.isAbsolute(depPath) || resolution.type !== undefined || !resolution['integrity']) {
|
||||
return resolution as LockfileResolution
|
||||
}
|
||||
const base = registry !== resolution['registry'] ? { registry: resolution['registry'] } : {}
|
||||
if (lockfileIncludeTarballUrl) {
|
||||
return {
|
||||
...base,
|
||||
integrity: resolution['integrity'],
|
||||
tarball: resolution['tarball'],
|
||||
}
|
||||
}
|
||||
// Sometimes packages are hosted under non-standard tarball URLs.
|
||||
// For instance, when they are hosted on npm Enterprise. See https://github.com/pnpm/pnpm/issues/867
|
||||
// Or in other weird cases, like https://github.com/pnpm/pnpm/issues/1072
|
||||
|
||||
Reference in New Issue
Block a user