feat!: remove store state

PR #2542
ref #594
This commit is contained in:
Zoltan Kochan
2020-05-10 12:02:46 +03:00
committed by GitHub
parent 471149e669
commit da091c7114
32 changed files with 32 additions and 628 deletions

View File

@@ -0,0 +1,14 @@
---
"@pnpm/modules-cleaner": major
"@pnpm/package-requester": major
"@pnpm/package-store": major
"@pnpm/plugin-commands-installation": major
"@pnpm/plugin-commands-store": major
"@pnpm/server": major
"@pnpm/store-connection-manager": minor
"@pnpm/store-controller-types": major
"supi": minor
"@pnpm/types": major
---
Remove state from store. The store should not store the information about what projects on the computer use what dependencies. This information was needed for pruning in pnpm v4. Also, without this information, we cannot have the `pnpm store usages` command. So `pnpm store usages` is deprecated.

View File

@@ -157,15 +157,6 @@ export default async function prune (
}
}))
}
const addedDepPaths = R.difference(newDepPaths, oldDepPaths)
const addedPkgIds = new Set(R.props<string, string>(addedDepPaths, wantedPkgIdsByDepPaths))
await opts.storeController.updateConnections(path.dirname(opts.virtualStoreDir), {
addDependencies: Array.from(addedPkgIds),
prune: opts.pruneStore || false,
removeDependencies: Array.from(orphanPkgIds),
})
}
return new Set(orphanDepPaths)

View File

@@ -32,10 +32,7 @@ import {
RequestPackageOptions,
WantedDependency,
} from '@pnpm/store-controller-types'
import {
DependencyManifest,
StoreIndex,
} from '@pnpm/types'
import { DependencyManifest } from '@pnpm/types'
import loadJsonFile = require('load-json-file')
import * as fs from 'mz/fs'
import pDefer = require('p-defer')
@@ -73,7 +70,6 @@ export default function (
ignoreFile?: (filename: string) => boolean,
networkConcurrency?: number,
storeDir: string,
storeIndex: StoreIndex,
verifyStoreIntegrity: boolean,
},
): RequestPackageFunction & {
@@ -102,7 +98,6 @@ export default function (
getFilePathInCafs,
requestsQueue,
storeDir: opts.storeDir,
storeIndex: opts.storeIndex,
verifyStoreIntegrity: opts.verifyStoreIntegrity,
})
const requestPackage = resolveAndFetch.bind(null, {
@@ -268,7 +263,6 @@ function fetchToStore (
getFilePathInCafs: (integrity: string, fileType: FileType) => string,
getFilePathByModeInCafs: (integrity: string, mode: number) => string,
requestsQueue: {add: <T>(fn: () => Promise<T>, opts: {priority: number}) => Promise<T>},
storeIndex: StoreIndex,
storeDir: string,
verifyStoreIntegrity: boolean,
},
@@ -489,7 +483,6 @@ function fetchToStore (
await fs.writeFile(path.join(target, TARBALL_INTEGRITY_FILENAME), opts.resolution['integrity'], 'utf8') // tslint:disable-line:no-string-literal
}
ctx.storeIndex[opts.pkgId] = ctx.storeIndex[opts.pkgId] || []
files.resolve({
filesIndex: integrity,
fromStore: false,

View File

@@ -40,11 +40,9 @@ const fetch = createFetcher({
test('request package', async t => {
const storeDir = tempy.directory()
t.comment(storeDir)
const storeIndex = {}
const requestPackage = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
t.equal(typeof requestPackage, 'function')
@@ -81,8 +79,6 @@ test('request package', async t => {
t.ok(pkgResponse.finishing!())
t.deepEqual(storeIndex, { 'registry.npmjs.org/is-positive/1.0.0': [] })
t.end()
})
@@ -90,7 +86,6 @@ test('request package but skip fetching', async t => {
const requestPackage = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir: '.store',
storeIndex: {},
verifyStoreIntegrity: true,
})
t.equal(typeof requestPackage, 'function')
@@ -130,7 +125,6 @@ test('request package but skip fetching, when resolution is already available',
const requestPackage = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir: '.store',
storeIndex: {},
verifyStoreIntegrity: true,
})
t.equal(typeof requestPackage, 'function')
@@ -200,12 +194,10 @@ test('refetch local tarball if its integrity has changed', async t => {
skipFetch: true,
update: false,
}
const storeIndex = {}
{
const requestPackage = createPackageRequester(localResolver as ResolveFunction, fetch, {
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
@@ -233,7 +225,6 @@ test('refetch local tarball if its integrity has changed', async t => {
{
const requestPackage = createPackageRequester(localResolver as ResolveFunction, fetch, {
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
@@ -255,7 +246,6 @@ test('refetch local tarball if its integrity has changed', async t => {
{
const requestPackage = createPackageRequester(localResolver as ResolveFunction, fetch, {
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
@@ -295,12 +285,10 @@ test('refetch local tarball if its integrity has changed. The requester does not
registry,
update: false,
}
const storeIndex = {}
{
const requestPackage = createPackageRequester(localResolver as ResolveFunction, fetch, {
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
@@ -322,7 +310,6 @@ test('refetch local tarball if its integrity has changed. The requester does not
{
const requestPackage = createPackageRequester(localResolver as ResolveFunction, fetch, {
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
@@ -341,7 +328,6 @@ test('refetch local tarball if its integrity has changed. The requester does not
{
const requestPackage = createPackageRequester(localResolver as ResolveFunction, fetch, {
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
@@ -363,7 +349,6 @@ test('fetchPackageToStore()', async (t) => {
const packageRequester = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir: tempy.directory(),
storeIndex: {},
verifyStoreIntegrity: true,
})
@@ -423,7 +408,6 @@ test('fetchPackageToStore() concurrency check', async (t) => {
const packageRequester = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir,
storeIndex: {},
verifyStoreIntegrity: true,
})
@@ -511,7 +495,6 @@ test('fetchPackageToStore() does not cache errors', async (t) => {
const packageRequester = createPackageRequester(resolve, noRetryFetch, {
networkConcurrency: 1,
storeDir: tempy.directory(),
storeIndex: {},
verifyStoreIntegrity: true,
})
@@ -563,7 +546,6 @@ test('always return a package manifest in the response', async t => {
const requestPackage = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir: tempy.directory(),
storeIndex: {},
verifyStoreIntegrity: true,
})
t.equal(typeof requestPackage, 'function')
@@ -622,7 +604,6 @@ test('fetchPackageToStore() fetch raw manifest of cached package', async (t) =>
const packageRequester = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir: tempy.directory(),
storeIndex: {},
verifyStoreIntegrity: true,
})
@@ -656,7 +637,6 @@ test('refetch package to store if it has been modified', async (t) => {
nock.cleanAll()
const storeDir = tempy.directory()
const cafsDir = path.join(storeDir, 'files')
const storeIndex = {}
const lockfileDir = tempy.directory()
t.comment(`store location: ${storeDir}`)
@@ -671,7 +651,6 @@ test('refetch package to store if it has been modified', async (t) => {
const packageRequester = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})
@@ -697,7 +676,6 @@ test('refetch package to store if it has been modified', async (t) => {
const packageRequester = createPackageRequester(resolve, fetch, {
networkConcurrency: 1,
storeDir,
storeIndex,
verifyStoreIntegrity: true,
})

View File

@@ -19,7 +19,6 @@
"@pnpm/cafs": "workspace:1.0.0-alpha.3",
"@pnpm/core-loggers": "workspace:4.0.1",
"@pnpm/fetcher-base": "workspace:7.0.0-alpha.2",
"@pnpm/fs-locker": "3.0.1",
"@pnpm/package-requester": "workspace:12.0.0-alpha.3",
"@pnpm/pkgid-to-filename": "3.0.0-0",
"@pnpm/resolver-base": "workspace:7.0.0",

View File

@@ -1,28 +0,0 @@
import { StoreIndex } from '@pnpm/types'
import loadJsonFile = require('load-json-file')
import path = require('path')
import writeJsonFile = require('write-json-file')
const STORE_JSON = 'store.json'
export async function read (storePath: string): Promise<StoreIndex | null> {
const storeJsonPath = path.join(storePath, STORE_JSON)
try {
return await loadJsonFile<StoreIndex>(storeJsonPath)
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
throw err
}
return null
}
}
export function save (storePath: string, store: StoreIndex) {
const storeJsonPath = path.join(storePath, STORE_JSON)
return writeJsonFile(storeJsonPath, store)
}
export function saveSync (storePath: string, store: StoreIndex) {
const storeJsonPath = path.join(storePath, STORE_JSON)
return writeJsonFile.sync(storeJsonPath, store)
}

View File

@@ -1,11 +1,5 @@
import { read, save } from './fs/storeIndex'
import createStore from './storeController'
export default createStore
export {
read,
save,
}
export * from '@pnpm/store-controller-types'

View File

@@ -1,28 +1,18 @@
import { getFilePathByModeInCafs as _getFilePathByModeInCafs } from '@pnpm/cafs'
import { FetchFunction } from '@pnpm/fetcher-base'
import lock from '@pnpm/fs-locker'
import { globalWarn } from '@pnpm/logger'
import createPackageRequester, { getCacheByEngine } from '@pnpm/package-requester'
import pkgIdToFilename from '@pnpm/pkgid-to-filename'
import { ResolveFunction } from '@pnpm/resolver-base'
import {
ImportPackageFunction,
PackageUsagesBySearchQueries,
StoreController,
} from '@pnpm/store-controller-types'
import { StoreIndex } from '@pnpm/types'
import rimraf = require('@zkochan/rimraf')
import pFilter = require('p-filter')
import pLimit from 'p-limit'
import path = require('path')
import exists = require('path-exists')
import R = require('ramda')
import writeJsonFile = require('write-json-file')
import {
read as readStore,
save as saveStore,
saveSync as saveStoreSync,
} from '../fs/storeIndex'
import createImportPackage from './createImportPackage'
import prune from './prune'
@@ -31,29 +21,17 @@ export default async function (
fetchers: {[type: string]: FetchFunction},
initOpts: {
ignoreFile?: (filename: string) => boolean,
locks?: string,
lockStaleDuration?: number,
storeDir: string,
networkConcurrency?: number,
packageImportMethod?: 'auto' | 'hardlink' | 'copy' | 'clone',
verifyStoreIntegrity: boolean,
},
): Promise<StoreController & { closeSync: () => void, saveStateSync: () => void }> {
): Promise<StoreController> {
const storeDir = initOpts.storeDir
const unlock = initOpts.locks
? await lock(initOpts.storeDir, {
locks: initOpts.locks,
stale: initOpts.lockStaleDuration || 60 * 1000, // 1 minute,
whenLocked: () => globalWarn(`waiting for the store at "${initOpts.storeDir}" to be unlocked...`),
})
: null
const storeIndex = await readStore(initOpts.storeDir) || {}
const packageRequester = createPackageRequester(resolve, fetchers, {
ignoreFile: initOpts.ignoreFile,
networkConcurrency: initOpts.networkConcurrency,
storeDir: initOpts.storeDir,
storeIndex,
verifyStoreIntegrity: initOpts.verifyStoreIntegrity,
})
@@ -69,20 +47,12 @@ export default async function (
}
return {
close: unlock ? async () => { await unlock() } : () => Promise.resolve(undefined),
closeSync: unlock ? () => unlock.sync() : () => undefined,
close: async () => {}, // tslint:disable-line:no-empty
fetchPackage: packageRequester.fetchPackageToStore,
findPackageUsages,
getPackageLocation,
importPackage,
prune: prune.bind(null, storeDir),
requestPackage: packageRequester.requestPackage,
saveState: saveStore.bind(null, initOpts.storeDir, storeIndex),
saveStateSync: saveStoreSync.bind(null, initOpts.storeDir, storeIndex),
updateConnections: async (prefix: string, opts: {addDependencies: string[], removeDependencies: string[], prune: boolean}) => {
await removeDependencies(prefix, opts.removeDependencies, { prune: opts.prune })
await addDependencies(prefix, opts.addDependencies)
},
upload,
}
@@ -110,47 +80,6 @@ export default async function (
}
}
async function removeDependencies (prefix: string, dependencyPkgIds: string[], opts: {prune: boolean}) {
await Promise.all(dependencyPkgIds.map(async (notDependent) => {
if (storeIndex[notDependent]) {
storeIndex[notDependent].splice(storeIndex[notDependent].indexOf(prefix), 1)
if (opts.prune && !storeIndex[notDependent].length) {
delete storeIndex[notDependent]
await rimraf(path.join(storeDir, notDependent))
}
}
}))
}
async function addDependencies (prefix: string, dependencyPkgIds: string[]) {
dependencyPkgIds.forEach((newDependent) => {
storeIndex[newDependent] = storeIndex[newDependent] || []
if (!storeIndex[newDependent].includes(prefix)) {
storeIndex[newDependent].push(prefix)
}
})
}
async function findPackageUsages (searchQueries: string[]): Promise<PackageUsagesBySearchQueries> {
const results = {} as PackageUsagesBySearchQueries
// FIXME Inefficient looping over all packages. Don't think there's a better way.
// Note we can't directly resolve packages because user may not specify package version
Object.keys(storeIndex).forEach(packageId => {
searchQueries
.filter((searchQuery) => packageId.indexOf(searchQuery) > -1)
.forEach((searchQuery) => {
results[searchQuery] = results[searchQuery] || []
results[searchQuery].push({
packageId,
usages: storeIndex[packageId] as string[],
})
})
})
return results
}
async function upload (builtPkgLocation: string, opts: {packageId: string, engine: string}) {
const filesIndex = await packageRequester.cafs.addFilesFromDir(builtPkgLocation)
// TODO: move this to a function
@@ -171,15 +100,3 @@ export default async function (
await writeJsonFile(path.join(cachePath, 'integrity.json'), integrity, { indent: undefined })
}
}
const limitExistsCheck = pLimit(10)
async function getRemovedProject (storeIndex: StoreIndex) {
const allProjects = R.uniq(R.unnest(Object.values(storeIndex)))
return pFilter(allProjects,
(projectPath: string) => limitExistsCheck(async () => {
const modulesDir = path.join(projectPath, 'node_modules')
return !await exists(modulesDir)
}))
}

View File

@@ -1,6 +1,6 @@
///<reference path="../../../typings/index.d.ts"/>
import createResolver from '@pnpm/npm-resolver'
import createStore, * as packageStore from '@pnpm/package-store'
import createStore from '@pnpm/package-store'
import { ResolveFunction } from '@pnpm/resolver-base'
import createFetcher from '@pnpm/tarball-fetcher'
import path = require('path')
@@ -8,11 +8,6 @@ import test = require('tape')
import tempy = require('tempy')
import './createImportPackage.spec'
test('public API', t => {
t.equal(typeof packageStore.read, 'function')
t.end()
})
test('store.importPackage()', async (t) => {
const storeDir = tempy.directory()
const registry = 'https://registry.npmjs.org/'

View File

@@ -104,16 +104,6 @@ export default async function recursive (
const store = await createOrConnectStoreController(opts)
// It is enough to save the store.json file once,
// once all installations are done.
// That's why saveState that is passed to the install engine
// does nothing.
const saveState = store.ctrl.saveState
const storeController = {
...store.ctrl,
saveState: async () => undefined,
}
const workspacePackages = cmdFullName !== 'unlink'
? arrayOfWorkspacePackagesToMap(allProjects)
: {}
@@ -123,7 +113,7 @@ export default async function recursive (
peer: opts.savePeer,
pruneLockfileImporters: (!opts.ignoredPackages || opts.ignoredPackages.size === 0)
&& pkgs.length === allProjects.length,
storeController,
storeController: store.ctrl,
storeDir: store.dir,
targetDependenciesField,
workspacePackages,
@@ -336,7 +326,7 @@ export default async function recursive (
...installOpts.rawConfig,
...localConfig,
},
storeController,
storeController: store.ctrl,
},
)
if (opts.save !== false) {
@@ -361,11 +351,6 @@ export default async function recursive (
}),
))
await saveState()
// The store should be unlocked because otherwise rebuild will not be able
// to access it
await storeController.close()
if (
!opts.lockfileOnly && !opts.ignoreScripts && (
cmdFullName === 'add' ||

View File

@@ -3,7 +3,6 @@ import { Config, types as allTypes } from '@pnpm/config'
import PnpmError from '@pnpm/error'
import logger, { globalInfo, LogBase } from '@pnpm/logger'
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { PackageUsages } from '@pnpm/store-controller-types'
import storePath from '@pnpm/store-path'
import archy = require('archy')
import { oneLine } from 'common-tags'
@@ -12,7 +11,6 @@ import renderHelp = require('render-help')
import storeAdd from './storeAdd'
import storePrune from './storePrune'
import storeStatus from './storeStatus'
import storeUsages from './storeUsages'
export const rcOptionsTypes = cliOptionsTypes
@@ -45,10 +43,6 @@ export function help () {
description: 'Adds new packages to the store. Example: pnpm store add express@4 typescript@2.1.0',
name: 'add <pkg>...',
},
{
description: 'Lists all pnpm projects on the current filesystem that depend on the specified packages. Example: pnpm store usages flatmap-stream',
name: 'usages <pkg>...',
},
{
description: oneLine`
Removes unreferenced (extraneous, orphan) packages from the store.
@@ -98,14 +92,6 @@ export async function handler (opts: StoreCommandOptions, params: string[]) {
storeController: store.ctrl,
tag: opts.tag,
})
case 'usages':
store = await createOrConnectStoreController(opts)
const packageSelectors = params.slice(1)
const packageUsagesBySelectors = await storeUsages(packageSelectors, {
reporter: opts.reporter,
storeController: store.ctrl,
})
return prettyPrintUsages(packageSelectors, packageUsagesBySelectors)
default:
return help()
if (params[0]) {
@@ -128,54 +114,3 @@ async function statusCmd (opts: StoreCommandOptions) {
throw new StoreStatusError(modifiedPkgs)
}
/**
* Uses archy to output package usages in a directory-tree like format.
* @param packageUsages a list of PackageUsage, one per query
*/
function prettyPrintUsages (selectors: string[], packageUsagesBySelectors: { [packageSelector: string]: PackageUsages[] }) {
// Create nodes for top level usage response
const packageUsageNodes: archy.Data[] = selectors.map((selector) => {
// Create label for root node
const label = `Package: ${selector}`
if (!packageUsagesBySelectors[selector].length) {
// If not found in store, just output string
return {
label,
nodes: [
'Not found in store',
],
} as archy.Data
}
// This package was found in the store, create children for all package ids
const foundPackagesNodes: archy.Data[] = packageUsagesBySelectors[selector].map((foundPackage) => {
const label = `Package in store: ${foundPackage.packageId}`
// Now create children for all locations this package id is used
const locations: string[] = foundPackage.usages
const locationNodes: archy.Data[] = locations.map(location => {
return {
label: 'Project with dependency: ' + location,
} as archy.Data
})
// Now create node for the package found in the store
return {
label,
nodes: locationNodes.length === 0 ? ['No pnpm projects using this package'] : locationNodes,
} as archy.Data
})
// Now create node for the original query
return {
label,
nodes: foundPackagesNodes,
} as archy.Data
})
const rootTrees = packageUsageNodes.map(node => archy(node))
return rootTrees.join('\n')
}

View File

@@ -45,8 +45,6 @@ export default async function (
}
}))
await opts.storeController.saveState()
if (reporter) {
streamParser.removeListener('data', reporter)
}

View File

@@ -13,7 +13,6 @@ export default async function (
streamParser.on('data', reporter)
}
await opts.storeController.prune()
await opts.storeController.saveState()
await opts.storeController.close()
if (reporter) {

View File

@@ -1,43 +0,0 @@
import { streamParser } from '@pnpm/logger'
import parseWantedDependency from '@pnpm/parse-wanted-dependency'
import { PackageUsages, StoreController } from '@pnpm/store-controller-types'
import { ReporterFunction } from './types'
export default async function (
packageSelectors: string[],
opts: {
reporter?: ReporterFunction,
storeController: StoreController,
},
): Promise<{ [packageSelector: string]: PackageUsages[] }> {
const reporter = opts?.reporter
if (reporter) {
streamParser.on('data', reporter)
}
const packageSelectorsBySearchQueries = packageSelectors.reduce((acc, packageSelector) => {
const searchQuery = parsedPackageSelectorToSearchQuery(parseWantedDependency(packageSelector))
acc[searchQuery] = packageSelector
return acc
}, {})
const packageUsagesBySearchQueries = await opts.storeController.findPackageUsages(Object.keys(packageSelectorsBySearchQueries))
const results = {}
for (const searchQuery of Object.keys(packageSelectorsBySearchQueries)) {
results[packageSelectorsBySearchQueries[searchQuery]] = packageUsagesBySearchQueries[searchQuery] || []
}
if (reporter) {
streamParser.removeListener('data', reporter)
}
return results
}
function parsedPackageSelectorToSearchQuery (parsedPackageSelector: {alias: string} | {pref: string} | {alias: string, pref: string}) {
if (!parsedPackageSelector['alias']) return parsedPackageSelector['pref']
if (!parsedPackageSelector['pref']) return `/${parsedPackageSelector['alias']}/`
return `/${parsedPackageSelector['alias']}/${parsedPackageSelector['pref']}`
}

View File

@@ -2,4 +2,3 @@
import './storeAdd'
import './storePrune'
import './storeStatus'
import './storeUsages'

View File

@@ -17,7 +17,6 @@ test('pnpm store add express@4.16.3', async function (t) {
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
@@ -28,14 +27,6 @@ test('pnpm store add express@4.16.3', async function (t) {
const { cafsHas } = assertStore(t, path.join(storeDir, STORE_VERSION))
await cafsHas('sha1-avilAjUNsyRuzEvs9rWjTSL37VM=')
const storeIndex = await loadJsonFile(path.join(storeDir, STORE_VERSION, 'store.json'))
t.deepEqual(
storeIndex,
{
[`localhost+${REGISTRY_MOCK_PORT}/express/4.16.3`]: [],
},
'package has been added to the store index',
)
t.end()
})
@@ -46,7 +37,6 @@ test('pnpm store add scoped package that uses not the standard registry', async
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: 'https://registry.npmjs.org/',
},
@@ -60,14 +50,6 @@ test('pnpm store add scoped package that uses not the standard registry', async
const { cafsHas } = assertStore(t, path.join(storeDir, STORE_VERSION))
await cafsHas('@foo/no-deps', '1.0.0')
const storeIndex = await loadJsonFile(path.join(storeDir, STORE_VERSION, 'store.json'))
t.deepEqual(
storeIndex,
{
[`localhost+${REGISTRY_MOCK_PORT}/@foo/no-deps/1.0.0`]: [],
},
'package has been added to the store index',
)
t.end()
})
@@ -81,7 +63,6 @@ test('should fail if some packages can not be added', async (t) => {
try {
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: 'https://registry.npmjs.org/',
},

View File

@@ -27,7 +27,6 @@ test('remove unreferenced packages', async (t) => {
const reporter = sinon.spy()
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: REGISTRY,
},
@@ -46,7 +45,6 @@ test('remove unreferenced packages', async (t) => {
reporter.resetHistory()
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: REGISTRY,
},
@@ -76,7 +74,6 @@ test.skip('remove packages that are used by project that no longer exist', async
const reporter = sinon.spy()
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: REGISTRY,
},
@@ -117,7 +114,6 @@ test('keep dependencies used by others', async (t) => {
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: REGISTRY,
},
@@ -139,7 +135,6 @@ test('keep dependency used by package', async (t) => {
await store.handler({
dir: process.cwd(),
lock: true,
rawConfig: {
registry: REGISTRY,
},

View File

@@ -23,7 +23,6 @@ test('CLI fails when store status finds modified packages', async function (t) {
try {
await store.handler({
dir: process.cwd(),
lock: false,
rawConfig: {
registry: REGISTRY,
},
@@ -49,7 +48,6 @@ test('CLI does not fail when store status does not find modified packages', asyn
await store.handler({
dir: process.cwd(),
lock: false,
rawConfig: {
registry: REGISTRY,
},

View File

@@ -1,152 +0,0 @@
import assertStore from '@pnpm/assert-store'
import { store } from '@pnpm/plugin-commands-store'
import prepare, { prepareEmpty } from '@pnpm/prepare'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import { stripIndent } from 'common-tags'
import execa = require('execa')
import path = require('path')
import exists = require('path-exists')
import ssri = require('ssri')
import test = require('tape')
const STORE_VERSION = 'v3'
const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}/`
const DEFAULT_OPTS = {
lock: false,
rawConfig: {
registry: REGISTRY,
},
registries: { default: REGISTRY },
}
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.js')
test('find usages for single package in store and in a project', async (t) => {
const project = prepare(t)
const storeDir = path.resolve('store')
// Install deps
await execa('node', [pnpmBin, 'add', 'is-negative@2.1.0', 'is-odd@3.0.0', '--store-dir', storeDir, '--registry', REGISTRY])
await project.cafsHas(ssri.fromHex('f0d86377aa15a64c34961f38ac2a9be2b40a1187', 'sha1').toString())
{
const output = await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['usages', 'is-negative'])
t.equal(
output,
stripIndent`
Package: is-negative
└─┬ Package in store: localhost+${REGISTRY_MOCK_PORT}/is-negative/2.1.0
└── Project with dependency: ${path.resolve('node_modules')}` + '\n',
'finds usages by package name',
)
}
{
const output = await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['usages', 'is-negative@2.1.0'])
t.equal(
output,
stripIndent`
Package: is-negative@2.1.0
└─┬ Package in store: localhost+${REGISTRY_MOCK_PORT}/is-negative/2.1.0
└── Project with dependency: ${path.resolve('node_modules')}` + '\n',
'finds usages by package name and version',
)
}
{
await project.storeHasNot('should-not-exist-uhsalzkj')
const output = await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['usages', 'should-not-exist-uhsalzkj'])
t.equal(
output,
stripIndent`
Package: should-not-exist-uhsalzkj
└── Not found in store` + '\n',
'cannot find usages if package does not exist',
)
}
{
const output = await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['usages', 'is-negative', 'is-odd'])
t.equal(
output,
stripIndent`
Package: is-negative
└─┬ Package in store: localhost+${REGISTRY_MOCK_PORT}/is-negative/2.1.0
└── Project with dependency: ${path.resolve('node_modules')}
Package: is-odd
└─┬ Package in store: localhost+${REGISTRY_MOCK_PORT}/is-odd/3.0.0
└── Project with dependency: ${path.resolve('node_modules')}` + '\n',
'finds usages of two packages',
)
}
t.end()
})
test('find usages for package(s) in store but not in any projects', async (t) => {
prepareEmpty(t)
const storeDir = path.resolve('store')
const { cafsHas } = assertStore(t, path.join(storeDir, STORE_VERSION))
// Add dependency directly to store (not to the project)
await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['add', 'is-negative@2.1.0'])
await cafsHas(ssri.fromHex('f0d86377aa15a64c34961f38ac2a9be2b40a1187', 'sha1').toString())
{
const output = await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['usages', 'is-negative'])
t.equal(
output,
stripIndent`
Package: is-negative
└─┬ Package in store: localhost+${REGISTRY_MOCK_PORT}/is-negative/2.1.0
└── No pnpm projects using this package` + '\n',
'finds usage of package',
)
}
await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['add', 'is-negative@2.0.0'])
await cafsHas(ssri.fromHex('09f4cb20dd1bddff37cb6630c618a9bc57915fd6', 'sha1').toString())
{
const output = await store.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
storeDir,
}, ['usages', 'is-negative'])
t.equal(
output,
stripIndent`
Package: is-negative
├─┬ Package in store: localhost+${REGISTRY_MOCK_PORT}/is-negative/2.1.0
│ └── No pnpm projects using this package
└─┬ Package in store: localhost+${REGISTRY_MOCK_PORT}/is-negative/2.0.0
└── No pnpm projects using this package` + '\n',
'finds usages of packages',
)
}
t.end()
})

View File

@@ -480,9 +480,6 @@ test('recursive install with link-workspace-packages and shared-workspace-lockfi
const outputs = await import(path.resolve('output.json')) as string[]
t.deepEqual(outputs, ['is-positive', 'project-1'])
const storeJson = await loadJsonFile<object>(path.resolve('store/v3/store.json'))
t.deepEqual(storeJson[`localhost+${REGISTRY_MOCK_PORT}/is-negative/1.0.0`].length, 1, 'new connections saved in store.json')
await execPnpm(['recursive', 'install', 'pkg-with-1-dep', '--link-workspace-packages', '--shared-workspace-lockfile=true', '--store-dir', 'store'])
{

View File

@@ -3,7 +3,6 @@ import {
FetchPackageToStoreOptions,
PackageFilesResponse,
PackageResponse,
PackageUsagesBySearchQueries,
RequestPackageOptions,
StoreController,
WantedDependency,
@@ -31,9 +30,6 @@ export default function (
resolve({
close: async () => { return },
fetchPackage: fetchPackage.bind(null, remotePrefix, limitedFetch),
findPackageUsages: async (searchQueries: string[]): Promise<PackageUsagesBySearchQueries> => {
return await limitedFetch(`${remotePrefix}/findPackageUsages`, { searchQueries }) as PackageUsagesBySearchQueries
},
getPackageLocation: async (
packageId: string,
packageName: string,
@@ -61,16 +57,7 @@ export default function (
await limitedFetch(`${remotePrefix}/prune`, {})
},
requestPackage: requestPackage.bind(null, remotePrefix, limitedFetch),
saveState: async () => {
await limitedFetch(`${remotePrefix}/saveState`, {})
},
stop: async () => { await limitedFetch(`${remotePrefix}/stop`, {}) },
updateConnections: async (prefix: string, opts: {addDependencies: string[], removeDependencies: string[], prune: boolean}) => {
await limitedFetch(`${remotePrefix}/updateConnections`, {
opts,
prefix,
})
},
upload: async (builtPkgLocation: string, opts: {packageId: string, engine: string}) => {
await limitedFetch(`${remotePrefix}/upload`, {
builtPkgLocation,

View File

@@ -126,20 +126,11 @@ export default function (
delete rawManifestPromises[body.msgId]
res.end(JSON.stringify(manifestResponse))
break
case '/updateConnections':
body = await bodyPromise
await store.updateConnections(body.prefix, body.opts)
res.end(JSON.stringify('OK'))
break
case '/prune':
// Disable store pruning when a server is running
res.statusCode = 403
res.end()
break
case '/saveState':
await store.saveState()
res.end(JSON.stringify('OK'))
break
case '/importPackage':
const importPackageBody = (await bodyPromise) as any // tslint:disable-line:no-any
await store.importPackage(importPackageBody.to, importPackageBody.opts)
@@ -173,10 +164,6 @@ export default function (
res.end(JSON.stringify(pkgLocation))
break
}
case '/findPackageUsages':
body = await bodyPromise
res.end(JSON.stringify(await store.findPackageUsages(body.searchQueries)))
break
default:
res.statusCode = 404
const error = { error: `${req.url} does not match any route` }

View File

@@ -32,8 +32,6 @@ async function createStoreController (storeDir?: string) {
strictSsl: true,
})
return createStore(resolve, fetchers, {
locks: undefined,
lockStaleDuration: 100,
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
@@ -300,58 +298,6 @@ test('disallow store prune', async t => {
t.end()
})
test('find package usages', async t => {
const port = 5813
const hostname = '127.0.0.1'
const remotePrefix = `http://${hostname}:${port}`
const storeCtrlForServer = await createStoreController()
const server = createServer(storeCtrlForServer, {
hostname,
port,
})
const storeCtrl = await connectStoreController({ remotePrefix, concurrency: 100 })
const dependency = { alias: 'is-positive', pref: '1.0.0' }
const projectDir = process.cwd()
// First install a dependency
const requestResponse = await storeCtrl.requestPackage(
dependency,
{
downloadPriority: 0,
lockfileDir: projectDir,
preferredVersions: {},
projectDir,
registry,
sideEffectsCache: false,
},
)
await requestResponse.bundledManifest!()
await requestResponse.finishing!()
// For debugging purposes
await storeCtrl.saveState()
// Now check if usages shows up
const packageUsagesByPackageSelectors = await storeCtrl.findPackageUsages(['/is-positive/1.0.0'])
t.deepEqual(
packageUsagesByPackageSelectors,
{
'/is-positive/1.0.0': [
{
packageId: 'registry.npmjs.org/is-positive/1.0.0',
usages: [],
},
],
},
)
await server.close()
await storeCtrl.close()
t.end()
})
test('server should only allow POST', async (t) => {
const port = 5813
const hostname = '127.0.0.1'

View File

@@ -7,8 +7,6 @@ import createResolver, { CreateResolverOptions } from './createResolver'
export type CreateNewStoreControllerOptions = CreateResolverOptions & Pick<Config,
'alwaysAuth' |
'lock' |
'lockStaleDuration' |
'networkConcurrency' |
'packageImportMethod' |
'registry' |
@@ -22,7 +20,6 @@ export default async (
) => {
// TODO: either print a warning or just log if --no-lock is used
const sopts = Object.assign(opts, {
locks: opts.lock ? path.join(opts.storeDir, '_locks') : undefined,
registry: opts.registry || 'https://registry.npmjs.org/',
})
const resolve = createResolver(sopts)
@@ -31,8 +28,6 @@ export default async (
return {
ctrl: await createStore(resolve, fetchers as {}, {
ignoreFile: sopts.ignoreFile,
locks: sopts.locks,
lockStaleDuration: sopts.lockStaleDuration,
networkConcurrency: sopts.networkConcurrency,
packageImportMethod: sopts.packageImportMethod,
storeDir: sopts.storeDir,

View File

@@ -41,20 +41,8 @@ export interface StoreController {
fetchPackage: FetchPackageToStoreFunction,
importPackage: ImportPackageFunction,
close (): Promise<void>,
updateConnections (prefix: string, opts: {addDependencies: string[], removeDependencies: string[], prune: boolean}): Promise<void>,
prune (): Promise<void>,
saveState (): Promise<void>,
upload (builtPkgLocation: string, opts: {packageId: string, engine: string}): Promise<void>,
findPackageUsages (searchQueries: string[]): Promise<PackageUsagesBySearchQueries>,
}
export type PackageUsagesBySearchQueries = {
[searchQuery: string]: PackageUsages[],
}
export type PackageUsages = {
packageId: string,
usages: string[], // paths to node projects
}
export type FetchPackageToStoreFunction = (

View File

@@ -153,19 +153,15 @@ export async function mutateModules (
}
let result!: Array<{ rootDir: string, manifest: ProjectManifest }>
try {
if (opts.lock) {
result = await lock(ctx.lockfileDir, _install, {
locks: opts.locks,
prefix: ctx.lockfileDir,
stale: opts.lockStaleDuration,
storeController: opts.storeController,
})
} else {
result = await _install()
}
} finally {
await opts.storeController.saveState()
if (opts.lock) {
result = await lock(ctx.lockfileDir, _install, {
locks: opts.locks,
prefix: ctx.lockfileDir,
stale: opts.lockStaleDuration,
storeController: opts.storeController,
})
} else {
result = await _install()
}
if (reporter) {

View File

@@ -1092,33 +1092,6 @@ test('subdep symlinks are updated if the lockfile has new subdep versions specif
t.ok(await exists(path.resolve(`node_modules/.pnpm/localhost+${REGISTRY_MOCK_PORT}/pkg-with-1-dep@100.0.0/node_modules/dep-of-pkg-with-1-dep/package.json`)))
})
test("store metadata is always saved, even if there's a fatal error", async (t: tape.Test) => {
prepareEmpty(t)
const opts = await testDefaults()
const saveStateSpy = sinon.spy()
opts.storeController.saveState = saveStateSpy
let err!: Error
try {
await mutateModules([
{
buildIndex: 0,
manifest: {
dependencies: {
'@pnpm/this-does-not-exist': '1.0.0',
},
},
mutation: 'install',
rootDir: process.cwd(),
},
], opts)
} catch (_err) {
err = _err
}
t.ok(err)
t.ok(saveStateSpy.calledOnce)
})
test('fail if none of the available resolvers support a version spec', async (t: tape.Test) => {
prepareEmpty(t)

View File

@@ -35,10 +35,6 @@ test('peer dependency is grouped with dependency when peer is resolved not from
t.ok(await exists(path.resolve(`node_modules/.pnpm/localhost+${REGISTRY_MOCK_PORT}/ajv-keywords@1.5.0_ajv@4.10.4/node_modules/ajv`)), 'peer dependency is linked')
t.equal(deepRequireCwd(['using-ajv', 'ajv-keywords', 'ajv', './package.json']).version, '4.10.4')
const storeIndex = await loadJsonFile<object>(path.join(opts.storeDir, 'store.json'))
t.ok(storeIndex[`localhost+${REGISTRY_MOCK_PORT}/ajv-keywords/1.5.0`], `localhost+${REGISTRY_MOCK_PORT}/ajv-keywords/1.5.0 added to store index`)
t.ok(storeIndex[`localhost+${REGISTRY_MOCK_PORT}/using-ajv/1.0.0`], `localhost+${REGISTRY_MOCK_PORT}/using-ajv/1.0.0 added to store index`)
// testing that peers are reinstalled correctly using info from the lockfile
await rimraf('node_modules')
await rimraf(path.resolve('..', '.store'))

View File

@@ -1,4 +1,3 @@
export * from './misc'
export * from './options'
export * from './package'
export * from './store'

View File

@@ -1,4 +0,0 @@
export interface StoreIndex {
// package ID => paths of dependent projects
[pkgId: string]: string[],
}

2
pnpm-lock.yaml generated
View File

@@ -1297,7 +1297,6 @@ importers:
'@pnpm/cafs': 'link:../cafs'
'@pnpm/core-loggers': 'link:../core-loggers'
'@pnpm/fetcher-base': 'link:../fetcher-base'
'@pnpm/fs-locker': 3.0.1
'@pnpm/package-requester': 'link:../package-requester'
'@pnpm/pkgid-to-filename': 3.0.0-0
'@pnpm/resolver-base': 'link:../resolver-base'
@@ -1332,7 +1331,6 @@ importers:
'@pnpm/cafs': 'workspace:1.0.0-alpha.3'
'@pnpm/core-loggers': 'workspace:4.0.1'
'@pnpm/fetcher-base': 'workspace:7.0.0-alpha.2'
'@pnpm/fs-locker': 3.0.1
'@pnpm/logger': 3.2.2
'@pnpm/npm-resolver': 'workspace:*'
'@pnpm/package-requester': 'workspace:12.0.0-alpha.3'

View File

@@ -32,13 +32,11 @@
},
"repository": "https://github.com/pnpm/pnpm/blob/master/privatePackages/assert-store",
"scripts": {
"commit": "commit",
"commitmsg": "commitlint -e",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build",
"lint": "tslint -c ../../tslint.json src/**/*.ts test/**/*.ts",
"tsc": "rimraf lib && tsc",
"prepublishOnly": "pnpm run tsc",
"prepublishOnly": "pnpm run compile",
"pretest": "pnpm install -C test/fixture/project --force --no-shared-workspace-lockfile",
"test": "pnpm run tsc && ts-node test"
"test": "pnpm run compile && ts-node test"
},
"dependencies": {
"@pnpm/cafs": "workspace:^1.0.0-alpha.3",