feat: run prepare scripts of Git dependencies (#3394)

close #855
This commit is contained in:
Zoltan Kochan
2021-04-28 02:22:25 +03:00
committed by GitHub
parent 1084ca1a75
commit e6a2654a27
40 changed files with 480 additions and 145 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/git-fetcher": minor
"@pnpm/tarball-fetcher": minor
---
Packages fetched from Git should have their `devDependencies` installed in case they have a `prepare` script.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/prepare-package": major
---
Project created.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/fetcher-base": major
---
`importPackage()` and `tempDir()` added to the `Cafs` type.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/fetcher-base": minor
---
`PackageFileInfo`, `PackageFilesResponse`, and `ImportPackageFunction` types added.

View File

@@ -0,0 +1,9 @@
---
"@pnpm/package-requester": major
---
Breaking changes to the API of `packageRequester()`.
`resolve` and `fetchers` should be passed in through `options`, not as arguments.
`cafs` is not returned anymore. It should be passed in through `options` as well.

View File

@@ -0,0 +1,7 @@
---
"@pnpm/build-modules": major
"@pnpm/lifecycle": major
"@pnpm/plugin-commands-rebuild": major
---
`prepare` scripts of Git-hosted packages are not executed (they are executed during fetching by `@pnpm/git-fetcher`).

View File

@@ -89,7 +89,6 @@ async function buildDependency (
initCwd: opts.lockfileDir,
optional: depNode.optional,
pkgRoot: depNode.dir,
prepare: depNode.prepare,
rawConfig: opts.rawConfig,
rootModulesDir: opts.rootModulesDir,
scriptShell: opts.scriptShell,
@@ -172,7 +171,6 @@ export interface DependenciesGraphNode {
isBuilt?: boolean
optional: boolean
optionalDependencies: Set<string>
prepare: boolean
requiresBuild?: boolean
}

View File

@@ -1,6 +1,5 @@
import { createReadStream, promises as fs } from 'fs'
import { DeferredManifestPromise } from '@pnpm/fetcher-base'
import { PackageFileInfo } from '@pnpm/store-controller-types'
import { DeferredManifestPromise, PackageFileInfo } from '@pnpm/fetcher-base'
import rimraf from '@zkochan/rimraf'
import pLimit from 'p-limit'
import ssri from 'ssri'

View File

@@ -1,7 +1,6 @@
import { promises as fs, Stats } from 'fs'
import path from 'path'
import { PackageFileInfo } from '@pnpm/store-controller-types'
import { FileWriteResult } from '@pnpm/fetcher-base'
import { FileWriteResult, PackageFileInfo } from '@pnpm/fetcher-base'
import getStream from 'get-stream'
import pathTemp from 'path-temp'
import renameOverwrite from 'rename-overwrite'

View File

@@ -2,9 +2,33 @@ import { Resolution } from '@pnpm/resolver-base'
import { DependencyManifest } from '@pnpm/types'
import { IntegrityLike } from 'ssri'
export interface PackageFileInfo {
checkedAt?: number // Nullable for backward compatibility
integrity: string
mode: number
size: number
}
export interface PackageFilesResponse {
fromStore: boolean
filesIndex: Record<string, PackageFileInfo>
sideEffects?: Record<string, Record<string, PackageFileInfo>>
}
export type ImportPackageFunction = (
to: string,
opts: {
targetEngine?: string
filesResponse: PackageFilesResponse
force: boolean
}
) => Promise<{ isBuilt: boolean, importMethod: undefined | string }>
export interface Cafs {
addFilesFromDir: (dir: string, manifest?: DeferredManifestPromise) => Promise<FilesIndex>
addFilesFromTarball: (stream: NodeJS.ReadableStream, manifest?: DeferredManifestPromise) => Promise<FilesIndex>
importPackage: ImportPackageFunction
tempDir: () => Promise<string>
}
export interface FetchOptions {

View File

@@ -30,14 +30,16 @@
"homepage": "https://github.com/pnpm/pnpm/blob/master/packages/git-fetcher#readme",
"dependencies": {
"@pnpm/fetcher-base": "workspace:10.0.1",
"@pnpm/prepare-package": "workspace:0.0.0",
"@zkochan/rimraf": "^2.1.1",
"execa": "^5.0.0",
"tempy": "^1.0.0"
"execa": "^5.0.0"
},
"devDependencies": {
"@pnpm/cafs": "workspace:3.0.2",
"@pnpm/package-store": "workspace:12.0.3",
"@pnpm/types": "workspace:7.1.0",
"p-defer": "^3.0.0"
"p-defer": "^3.0.0",
"tempy": "^1.0.0"
},
"funding": "https://opencollective.com/pnpm"
}

View File

@@ -1,8 +1,8 @@
import path from 'path'
import { Cafs, DeferredManifestPromise } from '@pnpm/fetcher-base'
import preparePackage from '@pnpm/prepare-package'
import rimraf from '@zkochan/rimraf'
import execa from 'execa'
import tempy from 'tempy'
export default () => {
return {
@@ -17,14 +17,15 @@ export default () => {
manifest?: DeferredManifestPromise
}
) {
const tempLocation = tempy.directory()
const tempLocation = await cafs.tempDir()
await execGit(['clone', resolution.repo, tempLocation])
await execGit(['checkout', resolution.commit], { cwd: tempLocation })
await preparePackage(tempLocation)
// removing /.git to make directory integrity calculation faster
await rimraf(path.join(tempLocation, '.git'))
return {
filesIndex: await cafs.addFilesFromDir(tempLocation, opts.manifest),
}
const filesIndex = await cafs.addFilesFromDir(tempLocation, opts.manifest)
await rimraf(tempLocation)
return { filesIndex }
},
}
}

View File

@@ -1,5 +1,6 @@
/// <reference path="../../../typings/index.d.ts"/>
import createCafs from '@pnpm/cafs'
import path from 'path'
import { createCafsStore } from '@pnpm/package-store'
import createFetcher from '@pnpm/git-fetcher'
import { DependencyManifest } from '@pnpm/types'
import pDefer from 'p-defer'
@@ -10,7 +11,7 @@ test('fetch', async () => {
const fetch = createFetcher().git
const manifest = pDefer<DependencyManifest>()
const { filesIndex } = await fetch(
createCafs(cafsDir),
createCafsStore(cafsDir),
{
commit: 'c9b30e71d704cd30fa71f2edd1ecc7dcc4985493',
repo: 'https://github.com/kevva/is-positive.git',
@@ -25,3 +26,21 @@ test('fetch', async () => {
const name = (await manifest.promise).name
expect(name).toEqual('is-positive')
})
test('fetch a package from Git that has a prepare script', async () => {
const cafsDir = tempy.directory()
const fetch = createFetcher().git
const manifest = pDefer<DependencyManifest>()
const { filesIndex } = await fetch(
createCafsStore(cafsDir),
{
commit: 'd2916cab494f6cddc85c921ffa3befb600e00e0e',
repo: 'https://github.com/pnpm/test-git-fetch.git',
type: 'git',
},
{
manifest,
}
)
expect(filesIndex[`dist${path.sep}index.js`]).toBeTruthy()
})

View File

@@ -15,6 +15,9 @@
{
"path": "../fetcher-base"
},
{
"path": "../prepare-package"
},
{
"path": "../types"
}

View File

@@ -18,9 +18,7 @@ export {
}
export async function runPostinstallHooks (
opts: RunLifecycleHookOptions & {
prepare?: boolean
}
opts: RunLifecycleHookOptions
): Promise<boolean> {
const pkg = await readPackageJsonFromDir(opts.pkgRoot)
if (pkg.scripts == null) {
@@ -41,10 +39,6 @@ export async function runPostinstallHooks (
await runLifecycleHook('postinstall', pkg, opts)
}
if (opts.prepare && pkg.scripts.prepare) {
await runLifecycleHook('prepare', pkg, opts)
}
return pkg.scripts.preinstall != null ||
pkg.scripts.install != null ||
pkg.scripts.postinstall != null

View File

@@ -36,19 +36,3 @@ test('runPostinstallHooks()', async () => {
expect(loadJsonFile.sync(path.join(pkgRoot, 'output.json'))).toStrictEqual(['preinstall', 'install', 'postinstall'])
})
test('runPostinstallHooks() with prepare = true', async () => {
const pkgRoot = path.join(fixtures, 'with-many-scripts')
rimraf.sync(path.join(pkgRoot, 'output.json'))
await runPostinstallHooks({
depPath: '/with-many-scripts/1.0.0',
optional: false,
pkgRoot,
prepare: true,
rawConfig: {},
rootModulesDir,
unsafePerm: true,
})
expect(loadJsonFile.sync(path.join(pkgRoot, 'output.json'))).toStrictEqual(['preinstall', 'install', 'postinstall', 'prepare'])
})

View File

@@ -56,6 +56,7 @@
"devDependencies": {
"@pnpm/client": "workspace:3.1.0",
"@pnpm/logger": "^4.0.0",
"@pnpm/package-store": "workspace:12.0.3",
"@types/ncp": "^2.0.4",
"@types/normalize-path": "^3.0.0",
"@types/ramda": "0.27.39",

View File

@@ -2,4 +2,5 @@ import packageRequester from './packageRequester'
export default packageRequester
export { PackageResponse, PackageFilesResponse } from '@pnpm/store-controller-types'
export { PackageFilesResponse } from '@pnpm/fetcher-base'
export { PackageResponse } from '@pnpm/store-controller-types'

View File

@@ -1,6 +1,6 @@
import { createReadStream, promises as fs } from 'fs'
import path from 'path'
import createCafs, {
import {
checkFilesIntegrity as _checkFilesIntegrity,
FileType,
getFilePathByModeInCafs as _getFilePathByModeInCafs,
@@ -16,6 +16,7 @@ import {
FetchFunction,
FetchOptions,
FetchResult,
PackageFilesResponse,
} from '@pnpm/fetcher-base'
import logger from '@pnpm/logger'
import readPackage from '@pnpm/read-package-json'
@@ -28,7 +29,6 @@ import {
import {
BundledManifest,
FetchPackageToStoreFunction,
PackageFilesResponse,
PackageResponse,
RequestPackageFunction,
RequestPackageOptions,
@@ -66,16 +66,16 @@ const pickBundledManifest = R.pick([
])
export default function (
resolve: ResolveFunction,
fetchers: {[type: string]: FetchFunction},
opts: {
resolve: ResolveFunction
fetchers: {[type: string]: FetchFunction}
cafs: Cafs
ignoreFile?: (filename: string) => boolean
networkConcurrency?: number
storeDir: string
verifyStoreIntegrity: boolean
}
): RequestPackageFunction & {
cafs: Cafs
fetchPackageToStore: FetchPackageToStoreFunction
requestPackage: RequestPackageFunction
} {
@@ -89,9 +89,8 @@ export default function (
requestsQueue['concurrency'] = networkConcurrency // eslint-disable-line
const cafsDir = path.join(opts.storeDir, 'files')
const cafs = createCafs(cafsDir, opts.ignoreFile)
const getFilePathInCafs = _getFilePathInCafs.bind(null, cafsDir)
const fetch = fetcher.bind(null, fetchers, cafs)
const fetch = fetcher.bind(null, opts.fetchers, opts.cafs)
const fetchPackageToStore = fetchToStore.bind(null, {
checkFilesIntegrity: _checkFilesIntegrity.bind(null, cafsDir),
fetch,
@@ -105,12 +104,12 @@ export default function (
const requestPackage = resolveAndFetch.bind(null, {
fetchPackageToStore,
requestsQueue,
resolve,
resolve: opts.resolve,
storeDir: opts.storeDir,
verifyStoreIntegrity: opts.verifyStoreIntegrity,
})
return Object.assign(requestPackage, { cafs, fetchPackageToStore, requestPackage })
return Object.assign(requestPackage, { fetchPackageToStore, requestPackage })
}
async function resolveAndFetch (

View File

@@ -6,6 +6,7 @@ import { getFilePathInCafs, PackageFilesIndex } from '@pnpm/cafs'
import createClient from '@pnpm/client'
import { streamParser } from '@pnpm/logger'
import createPackageRequester, { PackageFilesResponse, PackageResponse } from '@pnpm/package-requester'
import { createCafsStore } from '@pnpm/package-store'
import { DependencyManifest } from '@pnpm/types'
import delay from 'delay'
import { depPathToFilename } from 'dependency-path'
@@ -28,7 +29,11 @@ const { resolve, fetchers } = createClient({
test('request package', async () => {
const storeDir = tempy.directory()
const requestPackage = createPackageRequester(resolve, fetchers, {
const cafs = createCafsStore(storeDir)
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
@@ -67,9 +72,14 @@ test('request package', async () => {
})
test('request package but skip fetching', async () => {
const requestPackage = createPackageRequester(resolve, fetchers, {
const storeDir = '.store'
const cafs = createCafsStore(storeDir)
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir: '.store',
storeDir,
verifyStoreIntegrity: true,
})
expect(typeof requestPackage).toBe('function')
@@ -103,9 +113,14 @@ test('request package but skip fetching', async () => {
})
test('request package but skip fetching, when resolution is already available', async () => {
const requestPackage = createPackageRequester(resolve, fetchers, {
const storeDir = '.store'
const cafs = createCafsStore(storeDir)
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir: '.store',
storeDir,
verifyStoreIntegrity: true,
})
expect(typeof requestPackage).toBe('function')
@@ -164,6 +179,7 @@ test('refetch local tarball if its integrity has changed', async () => {
const tarball = `file:${tarballRelativePath}`
const wantedPackage = { pref: tarball }
const storeDir = tempy.directory()
const cafs = createCafsStore(storeDir)
const pkgId = `file:${normalize(tarballRelativePath)}`
const requestPackageOpts = {
downloadPriority: 0,
@@ -176,7 +192,10 @@ test('refetch local tarball if its integrity has changed', async () => {
}
{
const requestPackage = createPackageRequester(resolve, fetchers, {
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
storeDir,
verifyStoreIntegrity: true,
})
@@ -208,7 +227,10 @@ test('refetch local tarball if its integrity has changed', async () => {
await delay(50)
{
const requestPackage = createPackageRequester(resolve, fetchers, {
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
storeDir,
verifyStoreIntegrity: true,
})
@@ -234,7 +256,10 @@ test('refetch local tarball if its integrity has changed', async () => {
}
{
const requestPackage = createPackageRequester(resolve, fetchers, {
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
storeDir,
verifyStoreIntegrity: true,
})
@@ -270,6 +295,7 @@ test('refetch local tarball if its integrity has changed. The requester does not
const tarball = `file:${tarballPath}`
const wantedPackage = { pref: tarball }
const storeDir = path.join(__dirname, '..', '.store')
const cafs = createCafsStore(storeDir)
const requestPackageOpts = {
downloadPriority: 0,
lockfileDir: projectDir,
@@ -280,7 +306,10 @@ test('refetch local tarball if its integrity has changed. The requester does not
}
{
const requestPackage = createPackageRequester(resolve, fetchers, {
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
storeDir,
verifyStoreIntegrity: true,
})
@@ -301,7 +330,10 @@ test('refetch local tarball if its integrity has changed. The requester does not
await delay(50)
{
const requestPackage = createPackageRequester(resolve, fetchers, {
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
storeDir,
verifyStoreIntegrity: true,
})
@@ -319,7 +351,10 @@ test('refetch local tarball if its integrity has changed. The requester does not
}
{
const requestPackage = createPackageRequester(resolve, fetchers, {
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
storeDir,
verifyStoreIntegrity: true,
})
@@ -337,9 +372,14 @@ test('refetch local tarball if its integrity has changed. The requester does not
})
test('fetchPackageToStore()', async () => {
const packageRequester = createPackageRequester(resolve, fetchers, {
const storeDir = tempy.directory()
const cafs = createCafsStore(storeDir)
const packageRequester = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir: tempy.directory(),
storeDir,
verifyStoreIntegrity: true,
})
@@ -404,7 +444,11 @@ test('fetchPackageToStore()', async () => {
test('fetchPackageToStore() concurrency check', async () => {
const storeDir = tempy.directory()
const cafsDir = path.join(storeDir, 'files')
const packageRequester = createPackageRequester(resolve, fetchers, {
const cafs = createCafsStore(storeDir)
const packageRequester = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
@@ -489,9 +533,14 @@ test('fetchPackageToStore() does not cache errors', async () => {
storeDir: '.pnpm',
})
const packageRequester = createPackageRequester(noRetry.resolve, noRetry.fetchers, {
const storeDir = tempy.directory()
const cafs = createCafsStore(storeDir)
const packageRequester = createPackageRequester({
resolve: noRetry.resolve,
fetchers: noRetry.fetchers,
cafs,
networkConcurrency: 1,
storeDir: tempy.directory(),
storeDir,
verifyStoreIntegrity: true,
})
@@ -538,9 +587,14 @@ test('fetchPackageToStore() does not cache errors', async () => {
// This test was added to cover the issue described here: https://github.com/pnpm/supi/issues/65
test('always return a package manifest in the response', async () => {
nock.cleanAll()
const requestPackage = createPackageRequester(resolve, fetchers, {
const storeDir = tempy.directory()
const cafs = createCafsStore(storeDir)
const requestPackage = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir: tempy.directory(),
storeDir,
verifyStoreIntegrity: true,
})
expect(typeof requestPackage).toBe('function')
@@ -598,9 +652,14 @@ test('fetchPackageToStore() fetch raw manifest of cached package', async () => {
.get('/is-positive/-/is-positive-1.0.0.tgz')
.replyWithFile(200, IS_POSTIVE_TARBALL)
const packageRequester = createPackageRequester(resolve, fetchers, {
const storeDir = tempy.directory()
const cafs = createCafsStore(storeDir)
const packageRequester = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir: tempy.directory(),
storeDir,
verifyStoreIntegrity: true,
})
@@ -651,7 +710,11 @@ test('refetch package to store if it has been modified', async () => {
let indexJsFile!: string
{
const packageRequester = createPackageRequester(resolve, fetchers, {
const cafs = createCafsStore(storeDir)
const packageRequester = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
@@ -681,7 +744,11 @@ test('refetch package to store if it has been modified', async () => {
streamParser.on('data', reporter)
{
const packageRequester = createPackageRequester(resolve, fetchers, {
const cafs = createCafsStore(storeDir)
const packageRequester = createPackageRequester({
resolve,
fetchers,
cafs,
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,

View File

@@ -1,5 +1,6 @@
import createStore from './storeController'
import createStore, { createCafsStore } from './storeController'
export default createStore
export { createCafsStore }
export * from '@pnpm/store-controller-types'

View File

@@ -1,7 +1,7 @@
import { promises as fs } from 'fs'
import path from 'path'
import {
import createCafs, {
getFilePathByModeInCafs as _getFilePathByModeInCafs,
PackageFileInfo,
PackageFilesIndex,
} from '@pnpm/cafs'
import { FetchFunction } from '@pnpm/fetcher-base'
@@ -9,36 +9,24 @@ import createPackageRequester from '@pnpm/package-requester'
import { ResolveFunction } from '@pnpm/resolver-base'
import {
ImportPackageFunction,
PackageFileInfo,
StoreController,
} from '@pnpm/store-controller-types'
import loadJsonFile from 'load-json-file'
import pathTemp from 'path-temp'
import writeJsonFile from 'write-json-file'
import createImportPackage from './createImportPackage'
import prune from './prune'
export default async function (
resolve: ResolveFunction,
fetchers: {[type: string]: FetchFunction},
initOpts: {
ignoreFile?: (filename: string) => boolean
storeDir: string
networkConcurrency?: number
function createPackageImporter (
opts: {
packageImportMethod?: 'auto' | 'hardlink' | 'copy' | 'clone'
verifyStoreIntegrity: boolean
cafsDir: string
}
): Promise<StoreController> {
const storeDir = initOpts.storeDir
const packageRequester = createPackageRequester(resolve, fetchers, {
ignoreFile: initOpts.ignoreFile,
networkConcurrency: initOpts.networkConcurrency,
storeDir: initOpts.storeDir,
verifyStoreIntegrity: initOpts.verifyStoreIntegrity,
})
const impPkg = createImportPackage(initOpts.packageImportMethod)
const cafsDir = path.join(storeDir, 'files')
const getFilePathByModeInCafs = _getFilePathByModeInCafs.bind(null, cafsDir)
const importPackage: ImportPackageFunction = async (to, opts) => {
): ImportPackageFunction {
const impPkg = createImportPackage(opts.packageImportMethod)
const getFilePathByModeInCafs = _getFilePathByModeInCafs.bind(null, opts.cafsDir)
return async (to, opts) => {
const filesMap = {} as Record<string, string>
let isBuilt!: boolean
let filesIndex!: Record<string, PackageFileInfo>
@@ -55,18 +43,66 @@ export default async function (
const importMethod = await impPkg(to, { filesMap, fromStore: opts.filesResponse.fromStore, force: opts.force })
return { importMethod, isBuilt }
}
}
export function createCafsStore (
storeDir: string,
opts?: {
ignoreFile?: (filename: string) => boolean
packageImportMethod?: 'auto' | 'hardlink' | 'copy' | 'clone'
}
) {
const cafsDir = path.join(storeDir, 'files')
const baseTempDir = path.join(storeDir)
const importPackage = createPackageImporter({
packageImportMethod: opts?.packageImportMethod,
cafsDir,
})
return {
...createCafs(cafsDir, opts?.ignoreFile),
importPackage,
tempDir: async () => {
const tmpDir = pathTemp(baseTempDir)
await fs.mkdir(tmpDir, { recursive: true })
return tmpDir
},
}
}
export default async function (
resolve: ResolveFunction,
fetchers: {[type: string]: FetchFunction},
initOpts: {
ignoreFile?: (filename: string) => boolean
storeDir: string
networkConcurrency?: number
packageImportMethod?: 'auto' | 'hardlink' | 'copy' | 'clone'
verifyStoreIntegrity: boolean
}
): Promise<StoreController> {
const storeDir = initOpts.storeDir
const cafs = createCafsStore(storeDir, initOpts)
const packageRequester = createPackageRequester({
resolve,
fetchers,
cafs,
ignoreFile: initOpts.ignoreFile,
networkConcurrency: initOpts.networkConcurrency,
storeDir: initOpts.storeDir,
verifyStoreIntegrity: initOpts.verifyStoreIntegrity,
})
return {
close: async () => {}, // eslint-disable-line:no-empty
fetchPackage: packageRequester.fetchPackageToStore,
importPackage,
importPackage: cafs.importPackage,
prune: prune.bind(null, storeDir),
requestPackage: packageRequester.requestPackage,
upload,
}
async function upload (builtPkgLocation: string, opts: {filesIndexFile: string, engine: string}) {
const sideEffectsIndex = await packageRequester.cafs.addFilesFromDir(builtPkgLocation)
const sideEffectsIndex = await cafs.addFilesFromDir(builtPkgLocation)
// TODO: move this to a function
// This is duplicated in @pnpm/package-requester
const integrity: Record<string, PackageFileInfo> = {}

View File

@@ -276,7 +276,6 @@ async function _rebuild (
extraBinPaths: ctx.extraBinPaths,
optional: pkgSnapshot.optional === true,
pkgRoot,
prepare: pkgSnapshot.prepare,
rawConfig: opts.rawConfig,
rootModulesDir: ctx.rootModulesDir,
shellEmulator: opts.shellEmulator,

View File

@@ -22,7 +22,7 @@ test('rebuilds dependencies', async () => {
'add',
'--save-dev',
'pre-and-postinstall-scripts-example',
'zkochan/install-scripts-example#prepare',
'pnpm/test-git-fetch#main',
'--registry',
REGISTRY,
'--store-dir',
@@ -33,7 +33,7 @@ test('rebuilds dependencies', async () => {
let modules = await project.readModulesManifest()
expect(modules!.pendingBuilds).toStrictEqual([
'/pre-and-postinstall-scripts-example/1.0.0',
'github.com/zkochan/install-scripts-example/2de638b8b572cd1e87b74f4540754145fb2c0ebb',
'github.com/pnpm/test-git-fetch/299c6d89507571462b992b92407a8a07663e32ee',
])
await rebuild.handler({
@@ -59,7 +59,7 @@ test('rebuilds dependencies', async () => {
}
{
const scripts = project.requireModule('install-scripts-example-for-pnpm/output.json')
const scripts = project.requireModule('test-git-fetch/output.json')
expect(scripts[0]).toBe('preinstall')
expect(scripts[1]).toBe('install')
expect(scripts[2]).toBe('postinstall')

View File

@@ -0,0 +1,15 @@
# @pnpm/prepare-package
> Prepares a Git-hosted package
[![npm version](https://img.shields.io/npm/v/@pnpm/prepare-package.svg)](https://www.npmjs.com/package/@pnpm/prepare-package)
## Installation
```sh
<pnpm|npm|yarn> add @pnpm/prepare-package
```
## License
MIT

View File

@@ -0,0 +1,3 @@
const config = require('../../jest.config.js');
module.exports = Object.assign({}, config, {});

View File

@@ -0,0 +1,36 @@
{
"name": "@pnpm/prepare-package",
"version": "0.0.0",
"description": "Prepares a Git-hosted package",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"files": [
"lib",
"!*.map"
],
"engines": {
"node": ">=12.17"
},
"scripts": {
"lint": "eslint -c ../../eslint.json src/**/*.ts",
"test": "pnpm run compile",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"
},
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/prepare-package",
"keywords": [
"pnpm"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"homepage": "https://github.com/pnpm/pnpm/blob/master/packages/prepare-package#readme",
"dependencies": {
"@pnpm/read-package-json": "workspace:5.0.1",
"@zkochan/rimraf": "^2.1.1",
"execa": "^5.0.0",
"preferred-pm": "^3.0.3"
},
"funding": "https://opencollective.com/pnpm"
}

View File

@@ -0,0 +1,14 @@
import path from 'path'
import { fromDir as readPackageJsonFromDir } from '@pnpm/read-package-json'
import rimraf from '@zkochan/rimraf'
import execa from 'execa'
import preferredPM from 'preferred-pm'
export default async function preparePackage (pkgDir: string) {
const manifest = await readPackageJsonFromDir(pkgDir)
if (manifest.scripts?.prepare != null && manifest.scripts.prepare !== '') {
const pm = (await preferredPM(pkgDir))?.name ?? 'npm'
await execa(pm, ['install'], { cwd: pkgDir })
await rimraf(path.join(pkgDir, 'node_modules'))
}
}

View File

@@ -0,0 +1,16 @@
{
"extends": "@pnpm/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src"
},
"include": [
"src/**/*.ts",
"../../typings/**/*.d.ts"
],
"references": [
{
"path": "../read-package-json"
}
]
}

View File

@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": [
"src/**/*.ts",
"test/**/*.ts",
"../../typings/**/*.d.ts"
]
}

View File

@@ -28,6 +28,7 @@
},
"homepage": "https://github.com/pnpm/pnpm/blob/master/packages/store-controller-types#readme",
"dependencies": {
"@pnpm/fetcher-base": "workspace:10.0.1",
"@pnpm/resolver-base": "workspace:8.0.1",
"@pnpm/types": "workspace:7.1.0"
},

View File

@@ -5,11 +5,18 @@ import {
WantedDependency,
WorkspacePackages,
} from '@pnpm/resolver-base'
import {
ImportPackageFunction,
PackageFileInfo,
PackageFilesResponse,
} from '@pnpm/fetcher-base'
import {
DependencyManifest,
PackageManifest,
} from '@pnpm/types'
export { PackageFileInfo, PackageFilesResponse, ImportPackageFunction }
export * from '@pnpm/resolver-base'
export type BundledManifest = Pick<
DependencyManifest,
@@ -58,28 +65,6 @@ export interface FetchPackageToStoreOptions {
}
}
export type ImportPackageFunction = (
to: string,
opts: {
targetEngine?: string
filesResponse: PackageFilesResponse
force: boolean
}
) => Promise<{ isBuilt: boolean, importMethod: undefined | string }>
export interface PackageFileInfo {
checkedAt?: number // Nullable for backward compatibility
integrity: string
mode: number
size: number
}
export interface PackageFilesResponse {
fromStore: boolean
filesIndex: Record<string, PackageFileInfo>
sideEffects?: Record<string, Record<string, PackageFileInfo>>
}
export type RequestPackageFunction = (
wantedDependency: WantedDependency,
options: RequestPackageOptions

View File

@@ -9,6 +9,9 @@
"../../typings/**/*.d.ts"
],
"references": [
{
"path": "../fetcher-base"
},
{
"path": "../resolver-base"
},

View File

@@ -284,18 +284,21 @@ test('run lifecycle scripts of dependent packages after running scripts of their
test('run prepare script for git-hosted dependencies', async () => {
const project = prepareEmpty()
await addDependenciesToPackage({}, ['zkochan/install-scripts-example#prepare'], await testDefaults({ fastUnpack: false }))
await addDependenciesToPackage({}, ['pnpm/test-git-fetch#299c6d89507571462b992b92407a8a07663e32ee'], await testDefaults({ fastUnpack: false }))
const scripts = project.requireModule('install-scripts-example-for-pnpm/output.json')
const scripts = project.requireModule('test-git-fetch/output.json')
expect(scripts).toStrictEqual([
'preinstall',
'install',
'postinstall',
'prepare',
'preinstall',
'install',
'postinstall',
])
const lockfile = await project.readLockfile()
expect(lockfile.packages['github.com/zkochan/install-scripts-example/2de638b8b572cd1e87b74f4540754145fb2c0ebb'].prepare === true).toBeTruthy()
expect(lockfile.packages['github.com/pnpm/test-git-fetch/299c6d89507571462b992b92407a8a07663e32ee'].prepare === true).toBeTruthy()
})
test('lifecycle scripts run before linking bins', async () => {

View File

@@ -37,13 +37,16 @@
"@pnpm/error": "workspace:2.0.0",
"@pnpm/fetcher-base": "workspace:10.0.1",
"@pnpm/fetching-types": "workspace:2.1.0",
"@pnpm/prepare-package": "workspace:0.0.0",
"@zkochan/retry": "^0.2.0",
"@zkochan/rimraf": "^2.1.1",
"ramda": "^0.27.1",
"ssri": "^8.0.1"
},
"devDependencies": {
"@pnpm/cafs": "workspace:3.0.2",
"@pnpm/fetch": "workspace:3.1.0",
"@pnpm/logger": "^4.0.0",
"@pnpm/package-store": "workspace:12.0.3",
"@types/retry": "^0.12.0",
"@types/rimraf": "^3.0.0",
"@types/ssri": "^7.1.0",

View File

@@ -6,9 +6,14 @@ import {
Cafs,
DeferredManifestPromise,
FetchResult,
FilesIndex,
PackageFileInfo,
} from '@pnpm/fetcher-base'
import { FetchFromRegistry } from '@pnpm/fetching-types'
import preparePackage from '@pnpm/prepare-package'
import * as retry from '@zkochan/retry'
import rimraf from '@zkochan/rimraf'
import R from 'ramda'
import ssri from 'ssri'
import { BadTarballError } from './errorTypes'
@@ -181,7 +186,11 @@ export default (
if (integrityCheckResult !== true) {
throw integrityCheckResult
}
resolve({ filesIndex: filesIndex })
if (!isGitHostedPkgUrl(url)) {
resolve({ filesIndex })
return
}
resolve({ filesIndex: await prepareGitHostedPkg(filesIndex, opts.cafs) })
} catch (err) {
reject(err)
}
@@ -195,6 +204,44 @@ export default (
}
}
function isGitHostedPkgUrl (url: string) {
return (
url.startsWith('https://codeload.github.com/') ||
url.startsWith('https://bitbucket.org/') ||
url.startsWith('https://gitlab.com/')
) && url.includes('tar.gz')
}
async function prepareGitHostedPkg (filesIndex: FilesIndex, cafs: Cafs) {
const tempLocation = await cafs.tempDir()
const filesIndexReady: Record<string, PackageFileInfo> = R.fromPairs(
await Promise.all(
Object.entries(filesIndex).map(async ([fileName, fileInfo]): Promise<[string, PackageFileInfo]> => {
const { integrity, checkedAt } = await fileInfo.writeResult
return [
fileName,
{
...R.omit(['writeResult'], fileInfo),
checkedAt,
integrity: integrity.toString(),
},
]
})
)
)
await cafs.importPackage(tempLocation, {
filesResponse: {
filesIndex: filesIndexReady,
fromStore: false,
},
force: true,
})
await preparePackage(tempLocation)
const newFilesIndex = await cafs.addFilesFromDir(tempLocation)
await rimraf(tempLocation)
return newFilesIndex
}
async function safeCheckStream (stream: any, integrity: string, url: string): Promise<true | Error> { // eslint-disable-line @typescript-eslint/no-explicit-any
try {
await ssri.checkStream(stream, integrity)

View File

@@ -1,9 +1,9 @@
/// <reference path="../../../typings/index.d.ts" />
import fs from 'fs'
import path from 'path'
import createCafs from '@pnpm/cafs'
import PnpmError, { FetchError } from '@pnpm/error'
import { createFetchFromRegistry } from '@pnpm/fetch'
import { createCafsStore } from '@pnpm/package-store'
import createFetcher, {
BadTarballError,
TarballIntegrityError,
@@ -14,7 +14,7 @@ import ssri from 'ssri'
import tempy from 'tempy'
const cafsDir = tempy.directory()
const cafs = createCafs(cafsDir)
const cafs = createCafsStore(cafsDir)
const tarballPath = path.join(__dirname, 'tars', 'babel-helper-hoist-variables-6.24.1.tgz')
const tarballSize = 1279

View File

@@ -9,9 +9,6 @@
"../../typings/**/*.d.ts"
],
"references": [
{
"path": "../cafs"
},
{
"path": "../core-loggers"
},
@@ -26,6 +23,9 @@
},
{
"path": "../fetching-types"
},
{
"path": "../prepare-package"
}
]
}

49
pnpm-lock.yaml generated
View File

@@ -667,6 +667,8 @@ importers:
'@pnpm/cafs': workspace:3.0.2
'@pnpm/fetcher-base': workspace:10.0.1
'@pnpm/git-fetcher': 'link:'
'@pnpm/package-store': workspace:12.0.3
'@pnpm/prepare-package': workspace:0.0.0
'@pnpm/types': workspace:7.1.0
'@zkochan/rimraf': ^2.1.1
execa: ^5.0.0
@@ -674,14 +676,16 @@ importers:
tempy: ^1.0.0
dependencies:
'@pnpm/fetcher-base': link:../fetcher-base
'@pnpm/prepare-package': link:../prepare-package
'@zkochan/rimraf': 2.1.1
execa: 5.0.0
tempy: 1.0.1
devDependencies:
'@pnpm/cafs': link:../cafs
'@pnpm/git-fetcher': 'link:'
'@pnpm/package-store': link:../package-store
'@pnpm/types': link:../types
p-defer: 3.0.0
tempy: 1.0.1
packages/git-resolver:
specifiers:
@@ -1470,6 +1474,7 @@ importers:
'@pnpm/fetcher-base': workspace:10.0.1
'@pnpm/logger': ^4.0.0
'@pnpm/package-requester': 'link:'
'@pnpm/package-store': workspace:12.0.3
'@pnpm/read-package-json': workspace:5.0.1
'@pnpm/resolver-base': workspace:8.0.1
'@pnpm/store-controller-types': workspace:11.0.1
@@ -1516,6 +1521,7 @@ importers:
'@pnpm/client': link:../client
'@pnpm/logger': 4.0.0
'@pnpm/package-requester': 'link:'
'@pnpm/package-store': link:../package-store
'@types/ncp': 2.0.4
'@types/normalize-path': 3.0.0
'@types/ramda': 0.27.39
@@ -2477,6 +2483,21 @@ importers:
'@pnpm/pnpmfile': 'link:'
'@types/ramda': 0.27.39
packages/prepare-package:
specifiers:
'@pnpm/prepare-package': 'link:'
'@pnpm/read-package-json': workspace:5.0.1
'@zkochan/rimraf': ^2.1.1
execa: ^5.0.0
preferred-pm: ^3.0.3
dependencies:
'@pnpm/read-package-json': link:../read-package-json
'@zkochan/rimraf': 2.1.1
execa: 5.0.0
preferred-pm: 3.0.3
devDependencies:
'@pnpm/prepare-package': 'link:'
packages/prune-lockfile:
specifiers:
'@pnpm/constants': workspace:5.0.0
@@ -2779,10 +2800,12 @@ importers:
packages/store-controller-types:
specifiers:
'@pnpm/fetcher-base': workspace:10.0.1
'@pnpm/resolver-base': workspace:8.0.1
'@pnpm/store-controller-types': 'link:'
'@pnpm/types': workspace:7.1.0
dependencies:
'@pnpm/fetcher-base': link:../fetcher-base
'@pnpm/resolver-base': link:../resolver-base
'@pnpm/types': link:../types
devDependencies:
@@ -2978,20 +3001,23 @@ importers:
packages/tarball-fetcher:
specifiers:
'@pnpm/cafs': workspace:3.0.2
'@pnpm/core-loggers': workspace:6.0.1
'@pnpm/error': workspace:2.0.0
'@pnpm/fetch': workspace:3.1.0
'@pnpm/fetcher-base': workspace:10.0.1
'@pnpm/fetching-types': workspace:2.1.0
'@pnpm/logger': ^4.0.0
'@pnpm/package-store': workspace:12.0.3
'@pnpm/prepare-package': workspace:0.0.0
'@pnpm/tarball-fetcher': 'link:'
'@types/retry': ^0.12.0
'@types/rimraf': ^3.0.0
'@types/ssri': ^7.1.0
'@zkochan/retry': ^0.2.0
'@zkochan/rimraf': ^2.1.1
cp-file: ^9.0.0
nock: 12.0.3
ramda: ^0.27.1
ssri: ^8.0.1
tempy: ^1.0.0
dependencies:
@@ -2999,12 +3025,15 @@ importers:
'@pnpm/error': link:../error
'@pnpm/fetcher-base': link:../fetcher-base
'@pnpm/fetching-types': link:../fetching-types
'@pnpm/prepare-package': link:../prepare-package
'@zkochan/retry': 0.2.0
'@zkochan/rimraf': 2.1.1
ramda: 0.27.1
ssri: 8.0.1
devDependencies:
'@pnpm/cafs': link:../cafs
'@pnpm/fetch': link:../fetch
'@pnpm/logger': 4.0.0
'@pnpm/package-store': link:../package-store
'@pnpm/tarball-fetcher': 'link:'
'@types/retry': 0.12.0
'@types/rimraf': 3.0.0
@@ -6873,6 +6902,7 @@ packages:
p-map: 4.0.0
rimraf: 3.0.2
slash: 3.0.0
dev: true
/delay/5.0.0:
resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==}
@@ -7778,7 +7808,6 @@ packages:
dependencies:
locate-path: 5.0.0
path-exists: 4.0.0
dev: true
/find-up/5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
@@ -7792,7 +7821,6 @@ packages:
dependencies:
micromatch: 4.0.4
pkg-dir: 4.2.0
dev: true
/findup/0.1.5:
resolution: {integrity: sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=}
@@ -8853,10 +8881,12 @@ packages:
/is-path-cwd/2.2.0:
resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
engines: {node: '>=6'}
dev: true
/is-path-inside/3.0.3:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
dev: true
/is-plain-obj/1.1.0:
resolution: {integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=}
@@ -9875,7 +9905,6 @@ packages:
js-yaml: 3.14.1
pify: 4.0.1
strip-bom: 3.0.0
dev: true
/locate-path/2.0.0:
resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=}
@@ -9890,7 +9919,6 @@ packages:
engines: {node: '>=8'}
dependencies:
p-locate: 4.1.0
dev: true
/locate-path/6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
@@ -10988,7 +11016,6 @@ packages:
engines: {node: '>=8'}
dependencies:
p-limit: 2.3.0
dev: true
/p-locate/5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
@@ -11300,7 +11327,6 @@ packages:
engines: {node: '>=8'}
dependencies:
find-up: 4.1.0
dev: true
/pkginfo/0.4.1:
resolution: {integrity: sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=}
@@ -11329,7 +11355,6 @@ packages:
find-yarn-workspace-root2: 1.2.16
path-exists: 4.0.0
which-pm: 2.0.0
dev: true
/prelude-ls/1.1.2:
resolution: {integrity: sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=}
@@ -12869,6 +12894,7 @@ packages:
/temp-dir/2.0.0:
resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
engines: {node: '>=8'}
dev: true
/tempy/1.0.1:
resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==}
@@ -12879,6 +12905,7 @@ packages:
temp-dir: 2.0.0
type-fest: 0.16.0
unique-string: 2.0.0
dev: true
/term-size/1.2.0:
resolution: {integrity: sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=}
@@ -13274,6 +13301,7 @@ packages:
/type-fest/0.16.0:
resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
engines: {node: '>=10'}
dev: true
/type-fest/0.18.1:
resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}
@@ -13767,7 +13795,6 @@ packages:
dependencies:
load-yaml-file: 0.2.0
path-exists: 4.0.0
dev: true
/which/1.3.1:
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}

View File

@@ -43,10 +43,20 @@ async function updateTSConfig (
...importer.devDependencies,
}
const references = [] as Array<{ path: string }>
for (const spec of Object.values(deps)) {
for (const [depName, spec] of Object.entries(deps)) {
if (!spec.startsWith('link:') || spec.length === 5) continue
const relativePath = spec.substr(5)
if (!await exists(path.join(dir, relativePath, 'tsconfig.json'))) continue
if (
depName === '@pnpm/package-store' && (
manifest.name === '@pnpm/git-fetcher' ||
manifest.name === '@pnpm/tarball-fetcher' ||
manifest.name === '@pnpm/package-requester'
)
) {
// This is to avoid a circular graph (which TypeScript references do not support.
continue
}
references.push({ path: relativePath })
}
await writeJsonFile(path.join(dir, 'tsconfig.lint.json'), {