Skip to content

Commit

Permalink
feat: add lockfile-include-tarball-url option (#5054)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
MBelniak and zkochan committed Jul 20, 2022
1 parent 557755e commit 406656f
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 10 deletions.
8 changes: 8 additions & 0 deletions .changeset/proud-items-fold.md
Original file line number Diff line number Diff line change
@@ -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).
1 change: 1 addition & 0 deletions packages/config/src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface Config {
saveOptional?: boolean
savePeer?: boolean
saveWorkspaceProtocol?: boolean | 'rolling'
lockfileIncludeTarballUrl?: boolean
scriptShell?: string
stream?: boolean
pnpmExecPath: string
Expand Down
2 changes: 2 additions & 0 deletions packages/config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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'],
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/install/extendInstallOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface StrictInstallOptions {
ignorePackageManifest: boolean
preferFrozenLockfile: boolean
saveWorkspaceProtocol: boolean | 'rolling'
lockfileIncludeTarballUrl: boolean
preferWorkspacePackages: boolean
preserveWorkspaceProtocol: boolean
scriptsPrependNodePath: boolean | 'warn-only'
Expand Down Expand Up @@ -155,6 +156,7 @@ const defaults = async (opts: InstallOptions) => {
rawConfig: {},
registries: DEFAULT_REGISTRIES,
saveWorkspaceProtocol: true,
lockfileIncludeTarballUrl: false,
scriptsPrependNodePath: false,
shamefullyHoist: false,
shellEmulator: false,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
11 changes: 11 additions & 0 deletions packages/core/test/lockfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
})
1 change: 1 addition & 0 deletions packages/plugin-commands-installation/src/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ export type InstallCommandOptions = Pick<Config,
| 'savePrefix'
| 'saveProd'
| 'saveWorkspaceProtocol'
| 'lockfileIncludeTarballUrl'
| 'selectedProjectsGraph'
| 'sideEffectsCache'
| 'sideEffectsCacheReadonly'
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-commands-installation/src/installDeps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type InstallDepsOptions = Pick<Config,
| 'savePrefix'
| 'saveProd'
| 'saveWorkspaceProtocol'
| 'lockfileIncludeTarballUrl'
| 'scriptsPrependNodePath'
| 'scriptShell'
| 'selectedProjectsGraph'
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-commands-installation/src/recursive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
| 'savePrefix'
| 'saveProd'
| 'saveWorkspaceProtocol'
| 'lockfileIncludeTarballUrl'
| 'sharedWorkspaceLockfile'
| 'tag'
> & {
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-commands-installation/src/update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function rcOptionsTypes () {
'lockfile-directory',
'lockfile-only',
'lockfile',
'lockfile-include-tarball-url',
'network-concurrency',
'noproxy',
'npmPath',
Expand Down
9 changes: 8 additions & 1 deletion packages/resolve-dependencies/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export default async function (
defaultUpdateDepth: number
preserveWorkspaceProtocol: boolean
saveWorkspaceProtocol: 'rolling' | boolean
lockfileIncludeTarballUrl?: boolean
}
) {
const _toResolveImporter = toResolveImporter.bind(null, {
Expand Down Expand Up @@ -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)) {
Expand Down
31 changes: 22 additions & 9 deletions packages/resolve-dependencies/src/updateLockfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
Expand All @@ -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 ?? {},
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 406656f

Please sign in to comment.