Skip to content
Merged
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
2 changes: 1 addition & 1 deletion env/node.fetcher/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ async function downloadAndUnpackTarballToDir (

cafs.importPackage(targetDir, {
filesResponse: {
filesIndex: filesIndex as Record<string, string>,
filesIndex,
resolvedFrom: 'remote',
requiresBuild: false,
},
Expand Down
13 changes: 5 additions & 8 deletions exec/lifecycle/src/runLifecycleHooksConcurrently.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,19 @@ export async function runLifecycleHooksConcurrently (
await Promise.all(
targetDirs.map(async (targetDir) => {
const targetModulesDir = path.join(targetDir, 'node_modules')
const nodeModulesIndex = {}
const newFilesIndex = new Map(filesResponse.filesIndex)
if (fs.existsSync(targetModulesDir)) {
// If the target directory contains a node_modules directory
// (it may happen when the hoisted node linker is used)
// then we need to preserve this node_modules.
// So we scan this node_modules directory and pass it as part of the new package.
await scanDir('node_modules', targetModulesDir, targetModulesDir, nodeModulesIndex)
await scanDir('node_modules', targetModulesDir, targetModulesDir, newFilesIndex)
}
return opts.storeController.importPackage(targetDir, {
filesResponse: {
resolvedFrom: 'local-dir',
...filesResponse,
filesIndex: {
...filesResponse.filesIndex,
...nodeModulesIndex,
},
filesIndex: newFilesIndex,
},
force: false,
})
Expand All @@ -101,7 +98,7 @@ export async function runLifecycleHooksConcurrently (
await runGroups(childConcurrency, groups)
}

async function scanDir (prefix: string, rootDir: string, currentDir: string, index: Record<string, string>): Promise<void> {
async function scanDir (prefix: string, rootDir: string, currentDir: string, index: Map<string, string>): Promise<void> {
const files = await fs.promises.readdir(currentDir)
await Promise.all(files.map(async (file) => {
const fullPath = path.join(currentDir, file)
Expand All @@ -111,7 +108,7 @@ async function scanDir (prefix: string, rootDir: string, currentDir: string, ind
}
if (stat.isFile()) {
const relativePath = path.relative(rootDir, fullPath)
index[path.join(prefix, relativePath)] = fullPath
index.set(path.join(prefix, relativePath), fullPath)
}
}))
}
15 changes: 11 additions & 4 deletions exec/pkg-requires-build/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type DependencyManifest } from '@pnpm/types'

export function pkgRequiresBuild (manifest: Partial<DependencyManifest> | undefined, filesIndex: Record<string, unknown>): boolean {
export function pkgRequiresBuild (manifest: Partial<DependencyManifest> | undefined, filesIndex: Map<string, unknown>): boolean {
return Boolean(
manifest?.scripts != null && (
Boolean(manifest.scripts.preinstall) ||
Expand All @@ -11,7 +11,14 @@ export function pkgRequiresBuild (manifest: Partial<DependencyManifest> | undefi
)
}

function filesIncludeInstallScripts (filesIndex: Record<string, unknown>): boolean {
return filesIndex['binding.gyp'] != null ||
Object.keys(filesIndex).some((filename) => !(filename.match(/^\.hooks[\\/]/) == null)) // TODO: optimize this
function filesIncludeInstallScripts (filesIndex: Map<string, unknown>): boolean {
if (filesIndex.has('binding.gyp')) {
return true
}
for (const filename of filesIndex.keys()) {
if (filename.match(/^\.hooks[\\/]/) != null) {
return true
}
}
return false
}
4 changes: 2 additions & 2 deletions exec/plugin-commands-rebuild/src/implementation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ async function _rebuild (
sideEffectsCacheKey = calcDepState(depGraph, depsStateCache, depPath, {
includeDepGraphHash: true,
})
if (pkgFilesIndex.sideEffects?.[sideEffectsCacheKey]) {
if (pkgFilesIndex.sideEffects?.has(sideEffectsCacheKey)) {
pkgsThatWereRebuilt.add(depPath)
return
}
Expand All @@ -373,7 +373,7 @@ async function _rebuild (
if (pgkManifest != null) {
// This won't return the correct result for packages with binding.gyp as we don't pass the filesIndex to the function.
// However, currently rebuild doesn't work for such packages at all, which should be fixed.
requiresBuild = pkgRequiresBuild(pgkManifest, {})
requiresBuild = pkgRequiresBuild(pgkManifest, new Map())
}

const hasSideEffects = requiresBuild && allowBuild(pkgInfo.name, pkgInfo.version, depPath) && await runPostinstallHooks({
Expand Down
22 changes: 11 additions & 11 deletions exec/plugin-commands-rebuild/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ test('rebuilds dependencies', async () => {
}),
},
})}`
expect(cacheIntegrity).toHaveProperty(['sideEffects', sideEffectsKey, 'added', 'generated-by-postinstall.js'])
delete cacheIntegrity!.sideEffects![sideEffectsKey].added!['generated-by-postinstall.js']
expect(cacheIntegrity.sideEffects!.get(sideEffectsKey)!.added!.has('generated-by-postinstall.js')).toBeTruthy()
cacheIntegrity!.sideEffects!.get(sideEffectsKey)!.added!.delete('generated-by-postinstall.js')
})

test('skipIfHasSideEffectsCache', async () => {
Expand All @@ -112,17 +112,17 @@ test('skipIfHasSideEffectsCache', async () => {
const cacheIntegrityPath = getIndexFilePathInCafs(path.join(storeDir, STORE_VERSION), getIntegrity('@pnpm.e2e/pre-and-postinstall-scripts-example', '1.0.0'), '@pnpm.e2e/[email protected]')
let cacheIntegrity = readV8FileSync<PackageFilesIndex>(cacheIntegrityPath)!
const sideEffectsKey = `${ENGINE_NAME};deps=${hashObject({ '@pnpm.e2e/[email protected]': {} })}`
cacheIntegrity.sideEffects = {
[sideEffectsKey]: {
added: {
foo: {
cacheIntegrity.sideEffects = new Map([
[sideEffectsKey, {
added: new Map([
['foo', {
integrity: 'bar',
mode: 1,
size: 1,
},
},
},
}
}],
]),
}],
])
fs.writeFileSync(cacheIntegrityPath, v8.serialize(cacheIntegrity))

let modules = project.readModulesManifest()
Expand All @@ -147,7 +147,7 @@ test('skipIfHasSideEffectsCache', async () => {

cacheIntegrity = readV8FileSync<PackageFilesIndex>(cacheIntegrityPath)!
expect(cacheIntegrity!.sideEffects).toBeTruthy()
expect(cacheIntegrity).toHaveProperty(['sideEffects', sideEffectsKey, 'added', 'foo'])
expect(cacheIntegrity.sideEffects!.get(sideEffectsKey)!.added!.get('foo')).toBeTruthy()
})

test('rebuild does not fail when a linked package is present', async () => {
Expand Down
12 changes: 7 additions & 5 deletions fetching/directory-fetcher/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export type FetchFromDirOptions = Omit<DirectoryFetcherOptions, 'lockfileDir'> &

export interface FetchResult {
local: true
filesIndex: Record<string, string>
filesIndex: Map<string, string>
filesStats?: Record<string, Stats | null>
packageImportMethod: 'hardlink'
manifest: DependencyManifest
Expand Down Expand Up @@ -75,7 +75,7 @@ async function _fetchAllFilesFromDir (
dir: string,
relativeDir = ''
): Promise<Pick<FetchResult, 'filesIndex' | 'filesStats'>> {
const filesIndex: Record<string, string> = {}
const filesIndex = new Map<string, string>()
const filesStats: Record<string, Stats | null> = {}
const files = await fs.readdir(dir)
await Promise.all(files
Expand All @@ -87,10 +87,12 @@ async function _fetchAllFilesFromDir (
const relativeSubdir = `${relativeDir}${relativeDir ? '/' : ''}${file}`
if (stat.isDirectory()) {
const subFetchResult = await _fetchAllFilesFromDir(readFileStat, filePath, relativeSubdir)
Object.assign(filesIndex, subFetchResult.filesIndex)
for (const [key, value] of subFetchResult.filesIndex) {
filesIndex.set(key, value)
}
Object.assign(filesStats, subFetchResult.filesStats)
} else {
filesIndex[relativeSubdir] = filePath
filesIndex.set(relativeSubdir, filePath)
filesStats[relativeSubdir] = fileStatResult.stat
}
})
Expand Down Expand Up @@ -142,7 +144,7 @@ async function fileStat (filePath: string): Promise<FileStatResult | null> {

async function fetchPackageFilesFromDir (dir: string): Promise<FetchResult> {
const files = await packlist(dir)
const filesIndex: Record<string, string> = Object.fromEntries(files.map((file) => [file, path.join(dir, file)]))
const filesIndex = new Map<string, string>(files.map((file) => [file, path.join(dir, file)]))
// In a regular pnpm workspace it will probably never happen that a dependency has no package.json file.
// Safe read was added to support the Bit workspace in which the components have no package.json files.
// Related PR in Bit: https://github.com/teambit/bit/pull/5251
Expand Down
28 changes: 14 additions & 14 deletions fetching/directory-fetcher/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ test('fetch including only package files', async () => {

expect(fetchResult.local).toBe(true)
expect(fetchResult.packageImportMethod).toBe('hardlink')
expect(fetchResult.filesIndex['package.json']).toBe(path.resolve('package.json'))
expect(fetchResult.filesIndex.get('package.json')).toBe(path.resolve('package.json'))

// Only those files are included which would get published
expect(Object.keys(fetchResult.filesIndex).sort()).toStrictEqual([
expect(Array.from(fetchResult.filesIndex.keys()).sort()).toStrictEqual([
'index.js',
'package.json',
])
Expand All @@ -50,10 +50,10 @@ test('fetch including all files', async () => {

expect(fetchResult.local).toBe(true)
expect(fetchResult.packageImportMethod).toBe('hardlink')
expect(fetchResult.filesIndex['package.json']).toBe(path.resolve('package.json'))
expect(fetchResult.filesIndex.get('package.json')).toBe(path.resolve('package.json'))

// Only those files are included which would get published
expect(Object.keys(fetchResult.filesIndex).sort()).toStrictEqual([
expect(Array.from(fetchResult.filesIndex.keys()).sort()).toStrictEqual([
'index.js',
'package.json',
'test.js',
Expand All @@ -76,10 +76,10 @@ test('fetch a directory that has no package.json', async () => {
expect(fetchResult.manifest).toBeUndefined()
expect(fetchResult.local).toBe(true)
expect(fetchResult.packageImportMethod).toBe('hardlink')
expect(fetchResult.filesIndex['index.js']).toBe(path.resolve('index.js'))
expect(fetchResult.filesIndex.get('index.js')).toBe(path.resolve('index.js'))

// Only those files are included which would get published
expect(Object.keys(fetchResult.filesIndex).sort()).toStrictEqual([
expect(Array.from(fetchResult.filesIndex.keys()).sort()).toStrictEqual([
'index.js',
])
})
Expand All @@ -99,10 +99,10 @@ test('fetch does not fail on package with broken symlink', async () => {

expect(fetchResult.local).toBe(true)
expect(fetchResult.packageImportMethod).toBe('hardlink')
expect(fetchResult.filesIndex['package.json']).toBe(path.resolve('package.json'))
expect(fetchResult.filesIndex.get('package.json')).toBe(path.resolve('package.json'))

// Only those files are included which would get published
expect(Object.keys(fetchResult.filesIndex).sort()).toStrictEqual([
expect(Array.from(fetchResult.filesIndex.keys()).sort()).toStrictEqual([
'index.js',
'package.json',
])
Expand Down Expand Up @@ -131,9 +131,9 @@ describe('fetch resolves symlinked files to their real locations', () => {

expect(fetchResult.local).toBe(true)
expect(fetchResult.packageImportMethod).toBe('hardlink')
expect(fetchResult.filesIndex['package.json']).toBe(path.resolve('package.json'))
expect(fetchResult.filesIndex['index.js']).toBe(indexJsPath)
expect(fetchResult.filesIndex['src/index.js']).toBe(path.join(srcPath, 'index.js'))
expect(fetchResult.filesIndex.get('package.json')).toBe(path.resolve('package.json'))
expect(fetchResult.filesIndex.get('index.js')).toBe(indexJsPath)
expect(fetchResult.filesIndex.get('src/index.js')).toBe(path.join(srcPath, 'index.js'))
})
test('fetch does not resolve symlinked files to their real locations by default', async () => {
const fetcher = createDirectoryFetcher()
Expand All @@ -148,8 +148,8 @@ describe('fetch resolves symlinked files to their real locations', () => {

expect(fetchResult.local).toBe(true)
expect(fetchResult.packageImportMethod).toBe('hardlink')
expect(fetchResult.filesIndex['package.json']).toBe(path.resolve('package.json'))
expect(fetchResult.filesIndex['index.js']).toBe(path.resolve('index.js'))
expect(fetchResult.filesIndex['src/index.js']).toBe(path.resolve('src/index.js'))
expect(fetchResult.filesIndex.get('package.json')).toBe(path.resolve('package.json'))
expect(fetchResult.filesIndex.get('index.js')).toBe(path.resolve('index.js'))
expect(fetchResult.filesIndex.get('src/index.js')).toBe(path.resolve('src/index.js'))
})
})
6 changes: 3 additions & 3 deletions fetching/fetcher-base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type FetchFunction<FetcherResolution = Resolution, Options = FetchOptions
export interface FetchResult {
local?: boolean
manifest?: DependencyManifest
filesIndex: Record<string, string>
filesIndex: Map<string, string>
requiresBuild: boolean
integrity?: string
}
Expand All @@ -44,7 +44,7 @@ export interface GitFetcherOptions {
}

export interface GitFetcherResult {
filesIndex: Record<string, string>
filesIndex: Map<string, string>
manifest?: DependencyManifest
requiresBuild: boolean
}
Expand All @@ -60,7 +60,7 @@ export interface DirectoryFetcherOptions {

export interface DirectoryFetcherResult {
local: true
filesIndex: Record<string, string>
filesIndex: Map<string, string>
packageImportMethod: 'hardlink'
manifest?: DependencyManifest
requiresBuild: boolean
Expand Down
18 changes: 9 additions & 9 deletions fetching/git-fetcher/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ test('fetch', async () => {
filesIndexFile: path.join(storeDir, 'index.json'),
}
)
expect(filesIndex['package.json']).toBeTruthy()
expect(filesIndex.has('package.json')).toBeTruthy()
expect(manifest?.name).toBe('is-positive')
})

Expand All @@ -67,7 +67,7 @@ test('fetch a package from Git sub folder', async () => {
filesIndexFile: path.join(storeDir, 'index.json'),
}
)
expect(filesIndex['public/index.html']).toBeTruthy()
expect(filesIndex.has('public/index.html')).toBeTruthy()
})

test('prevent directory traversal attack when using Git sub folder', async () => {
Expand Down Expand Up @@ -129,7 +129,7 @@ test('fetch a package from Git that has a prepare script', async () => {
filesIndexFile: path.join(storeDir, 'index.json'),
}
)
expect(filesIndex['dist/index.js']).toBeTruthy()
expect(filesIndex.has('dist/index.js')).toBeTruthy()
})

// Test case for https://github.com/pnpm/pnpm/issues/1866
Expand All @@ -148,7 +148,7 @@ test('fetch a package without a package.json', async () => {
filesIndexFile: path.join(storeDir, 'index.json'),
}
)
expect(filesIndex['denolib.json']).toBeTruthy()
expect(filesIndex.has('denolib.json')).toBeTruthy()
})

// Covers the regression reported in https://github.com/pnpm/pnpm/issues/4064
Expand Down Expand Up @@ -191,7 +191,7 @@ test('still able to shallow fetch for allowed hosts', async () => {
// Discard final argument as it passes temporary directory
expect(calls[i].slice(0, -1)).toEqual(expectedCalls[i])
}
expect(filesIndex['package.json']).toBeTruthy()
expect(filesIndex.has('package.json')).toBeTruthy()
expect(manifest?.name).toBe('is-positive')
})

Expand Down Expand Up @@ -224,8 +224,8 @@ test('do not build the package when scripts are ignored', async () => {
}, {
filesIndexFile: path.join(storeDir, 'index.json'),
})
expect(filesIndex['package.json']).toBeTruthy()
expect(filesIndex['prepare.txt']).toBeFalsy()
expect(filesIndex.has('package.json')).toBeTruthy()
expect(filesIndex.has('prepare.txt')).toBeFalsy()
expect(globalWarn).toHaveBeenCalledWith('The git-hosted package fetched from "https://github.com/pnpm-e2e/prepare-script-works.git" has to be built but the build scripts were ignored.')
})

Expand Down Expand Up @@ -261,7 +261,7 @@ test('allow git package with prepare script', async () => {
allowBuild: (pkgName) => pkgName === '@pnpm.e2e/prepare-script-works',
filesIndexFile: path.join(storeDir, 'index.json'),
})
expect(filesIndex['package.json']).toBeTruthy()
expect(filesIndex.has('package.json')).toBeTruthy()
// Note: prepare.txt is in .gitignore so it won't be in the files index
// The fact that no error was thrown proves the prepare script was allowed to run
})
Expand All @@ -284,7 +284,7 @@ test('fetch only the included files', async () => {
filesIndexFile: path.join(storeDir, 'index.json'),
}
)
expect(Object.keys(filesIndex).sort()).toStrictEqual([
expect(Array.from(filesIndex.keys()).sort()).toStrictEqual([
'README.md',
'dist/index.js',
'package.json',
Expand Down
Loading
Loading