mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-28 12:01:37 -04:00
* chore: update all dependencies to latest versions Update all outdated dependencies across the monorepo catalog and fix breaking changes from major version bumps. Notable updates: - ESLint 9 → 10 (fix custom rule API, disable new no-useless-assignment) - @stylistic/eslint-plugin 4 → 5 (auto-fixed indent changes) - @cyclonedx/cyclonedx-library 9 → 10 (adapt to removed SPDX API) - esbuild 0.25 → 0.27 - TypeScript 5.9.2 → 5.9.3 - Various @types packages, test utilities, and build tools Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update unified/remark/mdast imports for v11/v4 API changes Update imports in get-release-text for the new ESM named exports: - mdast-util-to-string: default → { toString } - unified: default → { unified } Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve typecheck errors from dependency updates - isexe v4: use named import { sync } instead of default export - remark-parse/remark-stringify v11: add vfile as packageExtension dependency so TypeScript can resolve type declarations - get-release-text: remove unused @ts-expect-error directives Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert runtime dependency major version bumps Revert major version bumps for runtime dependencies that are bundled into pnpm to fix test failures where pnpm add silently fails: - bin-links: keep ^5.0.0 (was ^6.0.0) - cli-truncate: keep ^4.0.0 (was ^5.2.0) - delay: keep ^6.0.0 (was ^7.0.0) - filenamify: keep ^6.0.0 (was ^7.0.1) - find-up: keep ^7.0.0 (was ^8.0.0) - isexe: keep 2.0.0 (was 4.0.0) - normalize-newline: keep 4.1.0 (was 5.0.0) - p-queue: keep ^8.1.0 (was ^9.1.0) - ps-list: keep ^8.1.1 (was ^9.0.0) - string-length: keep ^6.0.0 (was ^7.0.1) - symlink-dir: keep ^7.0.0 (was ^9.0.0) - terminal-link: keep ^4.0.0 (was ^5.0.0) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore runtime dependency major version bumps Re-apply all runtime dependency major version bumps that were previously reverted. All packages maintain their default exports except isexe v4 which needs named imports. Updated runtime deps: - bin-links: ^5.0.0 → ^6.0.0 - cli-truncate: ^4.0.0 → ^5.2.0 - delay: ^6.0.0 → ^7.0.0 - filenamify: ^6.0.0 → ^7.0.1 - find-up: ^7.0.0 → ^8.0.0 - isexe: 2.0.0 → 4.0.0 (fix: use named import { sync }) - normalize-newline: 4.1.0 → 5.0.0 - p-queue: ^8.1.0 → ^9.1.0 - ps-list: ^8.1.1 → ^9.0.0 - string-length: ^6.0.0 → ^7.0.1 - symlink-dir: ^7.0.0 → ^9.0.0 - terminal-link: ^4.0.0 → ^5.0.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert tempy to 3.0.0 to fix bundle hang tempy 3.2.0 pulls in temp-dir 3.0.0 which uses async fs.realpath() inside its module init. When bundled by esbuild into the __esm lazy init pattern, this causes a deadlock during module initialization, making the pnpm binary hang silently on startup. Keeping tempy at 3.0.0 which uses temp-dir 2.x (sync fs.realpathSync). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add comment explaining why tempy cannot be upgraded Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert nock to 13.3.4 for node-fetch compatibility nock 14 changed its HTTP interception mechanism in a way that doesn't properly intercept node-fetch requests, causing audit tests to hang waiting for responses that are never intercepted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add comment explaining why nock cannot be upgraded Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update symlink-dir imports for v10 ESM named exports symlink-dir v10 removed the default export and switched to named exports: { symlinkDir, symlinkDirSync }. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert @typescript/native-preview to working version Newer tsgo dev builds (>= 20260318) have a regression where @types/node cannot be resolved, breaking all node built-in types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: vulnerabilities * fix: align comment indentation in runLifecycleHook Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pin msgpackr to 1.11.8 for TypeScript 5.9 compatibility msgpackr 1.11.9 has broken type definitions that use Iterable/Iterator without required type arguments, causing compile errors with TS 5.9. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
444 lines
12 KiB
TypeScript
444 lines
12 KiB
TypeScript
import { toOutput$ } from '@pnpm/cli.default-reporter'
|
|
import type { Config } from '@pnpm/config.reader'
|
|
import {
|
|
fetchingProgressLogger,
|
|
progressLogger,
|
|
stageLogger,
|
|
statsLogger,
|
|
} from '@pnpm/core-loggers'
|
|
import {
|
|
createStreamParser,
|
|
logger,
|
|
} from '@pnpm/logger'
|
|
import chalk from 'chalk'
|
|
import normalizeNewline from 'normalize-newline'
|
|
import { firstValueFrom } from 'rxjs'
|
|
import { map, skip, take, toArray } from 'rxjs/operators'
|
|
|
|
import { formatWarn } from '../src/reporterForClient/utils/formatWarn.js'
|
|
|
|
const hlValue = chalk.cyanBright
|
|
|
|
const EOL = '\n'
|
|
|
|
test('prints progress beginning', async () => {
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/project' } as Config,
|
|
},
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/project',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$)
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}`)
|
|
})
|
|
|
|
test('prints progress without added packages stats', async () => {
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/project' } as Config,
|
|
},
|
|
reportingOptions: {
|
|
hideAddedPkgsProgress: true,
|
|
},
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/project',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$)
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}`)
|
|
})
|
|
|
|
test('prints all progress stats', async () => {
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/project' } as Config,
|
|
},
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/project',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/project',
|
|
status: 'fetched',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/bar/1.0.0',
|
|
requester: '/src/project',
|
|
status: 'found_in_store',
|
|
})
|
|
progressLogger.debug({
|
|
method: 'hardlink',
|
|
requester: '/src/project',
|
|
status: 'imported',
|
|
to: '/node_modules/.pnpm/foo@1.0.0',
|
|
})
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$.pipe(skip(3), take(1)))
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('1')}, downloaded ${hlValue('1')}, added ${hlValue('1')}`)
|
|
})
|
|
|
|
test('prints progress beginning of node_modules from not cwd', async () => {
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/projects' } as Config,
|
|
},
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/projects/foo',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/projects/foo',
|
|
status: 'resolved',
|
|
})
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$)
|
|
expect(output).toBe(`foo | Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}`)
|
|
})
|
|
|
|
test('prints progress beginning of node_modules from not cwd, when progress prefix is hidden', async () => {
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/projects' } as Config,
|
|
},
|
|
streamParser: createStreamParser(),
|
|
reportingOptions: {
|
|
hideProgressPrefix: true,
|
|
},
|
|
})
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/projects/foo',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/projects/foo',
|
|
status: 'resolved',
|
|
})
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$)
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}`)
|
|
})
|
|
|
|
test('prints progress beginning when appendOnly is true', async () => {
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/project' } as Config,
|
|
},
|
|
reportingOptions: {
|
|
appendOnly: true,
|
|
},
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/project',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$)
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}`)
|
|
})
|
|
|
|
test('prints progress beginning during recursive install', async () => {
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: {
|
|
dir: '/src/project',
|
|
recursive: true,
|
|
} as Config,
|
|
},
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/project',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId: 'registry.npmjs.org/foo/1.0.0',
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$)
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}`)
|
|
})
|
|
|
|
test('prints progress on first download', async () => {
|
|
expect.assertions(1)
|
|
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/project' } as Config,
|
|
},
|
|
reportingOptions: { throttleProgress: 0 },
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
const packageId = 'registry.npmjs.org/foo/1.0.0'
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/project',
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId,
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
progressLogger.debug({
|
|
packageId,
|
|
requester: '/src/project',
|
|
status: 'fetched',
|
|
})
|
|
|
|
const output = await firstValueFrom(output$.pipe(skip(1), take(1)))
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('1')}, added ${hlValue('0')}`)
|
|
})
|
|
|
|
test('moves fixed line to the end', async () => {
|
|
expect.assertions(1)
|
|
const prefix = '/src/project'
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: prefix } as Config,
|
|
},
|
|
reportingOptions: { throttleProgress: 0 },
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
const packageId = 'registry.npmjs.org/foo/1.0.0'
|
|
|
|
stageLogger.debug({
|
|
prefix,
|
|
stage: 'resolution_started',
|
|
})
|
|
progressLogger.debug({
|
|
packageId,
|
|
requester: prefix,
|
|
status: 'resolved',
|
|
})
|
|
|
|
progressLogger.debug({
|
|
packageId,
|
|
requester: prefix,
|
|
status: 'fetched',
|
|
})
|
|
logger.warn({ message: 'foo', prefix })
|
|
|
|
stageLogger.debug({
|
|
prefix,
|
|
stage: 'resolution_done',
|
|
})
|
|
stageLogger.debug({
|
|
prefix,
|
|
stage: 'importing_done',
|
|
})
|
|
|
|
const output = await firstValueFrom(output$.pipe(skip(3), take(1), map(normalizeNewline)))
|
|
expect(output).toBe(formatWarn('foo') + EOL +
|
|
`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('1')}, added ${hlValue('0')}, done`)
|
|
})
|
|
|
|
test('prints "Already up to date"', async () => {
|
|
const output$ = toOutput$({
|
|
context: { argv: ['install'] },
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
const prefix = process.cwd()
|
|
|
|
statsLogger.debug({ added: 0, prefix })
|
|
statsLogger.debug({ removed: 0, prefix })
|
|
|
|
expect.assertions(1)
|
|
|
|
const output = await firstValueFrom(output$.pipe(take(1), map(normalizeNewline)))
|
|
expect(output).toBe('Already up to date')
|
|
})
|
|
|
|
test('prints progress of big files download', async () => {
|
|
expect.assertions(6)
|
|
|
|
const output$ = toOutput$({
|
|
context: {
|
|
argv: ['install'],
|
|
config: { dir: '/src/project' } as Config,
|
|
},
|
|
reportingOptions: { throttleProgress: 0 },
|
|
streamParser: createStreamParser(),
|
|
})
|
|
|
|
const pkgId1 = 'registry.npmjs.org/foo/1.0.0'
|
|
const pkgId2 = 'registry.npmjs.org/bar/2.0.0'
|
|
const pkgId3 = 'registry.npmjs.org/qar/3.0.0'
|
|
|
|
stageLogger.debug({
|
|
prefix: '/src/project',
|
|
stage: 'resolution_started',
|
|
})
|
|
|
|
progressLogger.debug({
|
|
packageId: pkgId1,
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
fetchingProgressLogger.debug({
|
|
attempt: 1,
|
|
packageId: pkgId1,
|
|
size: 1024 * 1024 * 10, // 10 MB
|
|
status: 'started',
|
|
})
|
|
|
|
fetchingProgressLogger.debug({
|
|
downloaded: 1024 * 1024 * 5.5, // 5.5 MB
|
|
packageId: pkgId1,
|
|
status: 'in_progress',
|
|
})
|
|
|
|
progressLogger.debug({
|
|
packageId: pkgId2,
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
fetchingProgressLogger.debug({
|
|
attempt: 1,
|
|
packageId: pkgId1,
|
|
size: 10, // 10 B
|
|
status: 'started',
|
|
})
|
|
|
|
fetchingProgressLogger.debug({
|
|
downloaded: 1024 * 1024 * 7,
|
|
packageId: pkgId1,
|
|
status: 'in_progress',
|
|
})
|
|
|
|
progressLogger.debug({
|
|
packageId: pkgId3,
|
|
requester: '/src/project',
|
|
status: 'resolved',
|
|
})
|
|
|
|
fetchingProgressLogger.debug({
|
|
attempt: 1,
|
|
packageId: pkgId3,
|
|
size: 1024 * 1024 * 20, // 20 MB
|
|
status: 'started',
|
|
})
|
|
|
|
fetchingProgressLogger.debug({
|
|
downloaded: 1024 * 1024 * 19, // 19 MB
|
|
packageId: pkgId3,
|
|
status: 'in_progress',
|
|
})
|
|
|
|
fetchingProgressLogger.debug({
|
|
downloaded: 1024 * 1024 * 10, // 10 MB
|
|
packageId: pkgId1,
|
|
status: 'in_progress',
|
|
})
|
|
|
|
const output = await firstValueFrom(output$.pipe(take(9), map(normalizeNewline), toArray()))
|
|
|
|
output.forEach((output, index) => {
|
|
switch (index) {
|
|
case 0:
|
|
expect(output).toBe(`Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}`)
|
|
return
|
|
case 1:
|
|
expect(output).toBe(`\
|
|
Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}
|
|
Downloading ${pkgId1}: ${hlValue('0.00 B')}/${hlValue('10.48 MB')}`)
|
|
return
|
|
case 2:
|
|
expect(output).toBe(`\
|
|
Progress: resolved ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}
|
|
Downloading ${pkgId1}: ${hlValue('5.76 MB')}/${hlValue('10.48 MB')}`)
|
|
return
|
|
case 4:
|
|
expect(output).toBe(`\
|
|
Progress: resolved ${hlValue('2')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}
|
|
Downloading ${pkgId1}: ${hlValue('7.34 MB')}/${hlValue('10.48 MB')}`)
|
|
return
|
|
case 7:
|
|
expect(output).toBe(`\
|
|
Progress: resolved ${hlValue('3')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}
|
|
Downloading ${pkgId1}: ${hlValue('7.34 MB')}/${hlValue('10.48 MB')}
|
|
Downloading ${pkgId3}: ${hlValue('19.92 MB')}/${hlValue('20.97 MB')}`)
|
|
return
|
|
case 8:
|
|
expect(output).toBe(`\
|
|
Downloading ${pkgId1}: ${hlValue('10.48 MB')}/${hlValue('10.48 MB')}, done
|
|
Progress: resolved ${hlValue('3')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}, added ${hlValue('0')}
|
|
Downloading ${pkgId3}: ${hlValue('19.92 MB')}/${hlValue('20.97 MB')}`)
|
|
return
|
|
}
|
|
})
|
|
})
|