feat: add layoutVersion to .modules.yaml

BREAKING CHANGE:

The new layout is not compatible with previous versions.
This commit is contained in:
zkochan
2017-06-22 22:01:48 +03:00
parent 1a99038178
commit ec6e577387
5 changed files with 15 additions and 102 deletions

View File

@@ -1,5 +1,5 @@
import {stripIndent, oneLine} from 'common-tags'
import {Modules} from '../fs/modulesController'
import {Modules, LAYOUT_VERSION} from '../fs/modulesController'
import {PnpmError, PnpmErrorCode} from '../errorTypes'
import semver = require('semver')
@@ -41,24 +41,6 @@ class BreakingChangeError extends PnpmError {
additionalInformation?: string
}
type StoreBreakingChangeErrorOptions = ErrorRelatedSources & {
storePath: string,
}
class StoreBreakingChangeError extends BreakingChangeError {
constructor (opts: StoreBreakingChangeErrorOptions) {
super({
code: 'STORE_BREAKING_CHANGE',
message: `The store structure was changed. Try running the same command with the --force parameter.`,
additionalInformation: opts.additionalInformation,
relatedIssue: opts.relatedIssue,
relatedPR: opts.relatedPR,
})
this.storePath = opts.storePath
}
storePath: string
}
type ModulesBreakingChangeErrorOptions = ErrorRelatedSources & {
modulesPath: string,
}
@@ -90,75 +72,11 @@ export default function checkCompatibility (
actualStorePath: opts.storePath,
})
}
if (!modules.packageManager) {
if (!modules.layoutVersion || modules.layoutVersion !== LAYOUT_VERSION) {
throw new ModulesBreakingChangeError({
modulesPath: opts.modulesPath,
additionalInformation: 'The change was needed to allow machine stores and dependency locks',
relatedPR: 524,
})
}
const pnpmVersion = getPackageManagerVersion(modules.packageManager)
check(pnpmVersion, opts.storePath, opts.modulesPath)
}
function getPackageManagerVersion(packageManager: string) {
// handle the case when the package is scoped: @scope/pkgname
if (packageManager.startsWith('@')) {
return packageManager.split('@')[2]
} else {
return packageManager.split('@')[1]
}
}
function check (pnpmVersion: string, storePath: string, modulesPath: string) {
if (!pnpmVersion || semver.lt(pnpmVersion, '0.28.0')) {
throw new StoreBreakingChangeError({
storePath,
relatedIssue: 276,
})
}
if (semver.lt(pnpmVersion, '0.33.0')) {
throw new StoreBreakingChangeError({
storePath,
additionalInformation: 'The change was needed to fix the GitHub rate limit issue',
relatedIssue: 361,
relatedPR: 363,
})
}
if (semver.lt(pnpmVersion, '0.37.0')) {
throw new StoreBreakingChangeError({
storePath,
additionalInformation: 'The structure of store.json/dependencies was changed to map dependencies to their fullnames',
})
}
if (semver.lt(pnpmVersion, '0.38.0')) {
throw new StoreBreakingChangeError({
storePath,
additionalInformation: 'The structure of store.json/dependencies was changed to not include the redundunt package.json at the end',
})
}
if (!pnpmVersion || semver.lt(pnpmVersion, '0.48.0')) {
throw new ModulesBreakingChangeError({ modulesPath, relatedPR: 534 })
}
if (semver.lt(pnpmVersion, '0.51.0')) {
throw new ModulesBreakingChangeError({ modulesPath, relatedPR: 576 })
}
if (semver.lt(pnpmVersion, '0.52.0')) {
throw new ModulesBreakingChangeError({ modulesPath, relatedPR: 593 })
}
if (semver.lt(pnpmVersion, '0.62.0')) {
throw new ModulesBreakingChangeError({
modulesPath,
relatedPR: 660,
additionalInformation: 'Information about the node_modules structure is stored in a node_modules/.shrinkwrap.yaml file instead of a node_modules/.graph.yaml file'
})
}
if (semver.lt(pnpmVersion, '0.64.0')) {
throw new ModulesBreakingChangeError({
modulesPath,
relatedPR: 694,
relatedIssue: 678,
additionalInformation: 'Packages having peer dependencies are linked to different variations. The variations depend on the set of resolved peer dependencies'
additionalInformation: 'The change was needed to make `independent-leafs` not the default installation layout',
relatedIssue: 821,
})
}
}

View File

@@ -27,7 +27,10 @@ import {
ResolvedDependencies,
} from 'pnpm-lockfile'
import {pkgIdToRef} from '../fs/shrinkwrap'
import {save as saveModules} from '../fs/modulesController'
import {
save as saveModules,
LAYOUT_VERSION,
} from '../fs/modulesController'
import mkdirp = require('mkdirp-promise')
import createMemoize, {MemoizedFunc} from '../memoize'
import {Package} from '../types'
@@ -381,6 +384,7 @@ async function installInContext (
packageManager: `${pnpmPkgJson.name}@${pnpmPkgJson.version}`,
storePath: ctx.storePath,
skipped: Array.from(installCtx.skipped),
layoutVersion: LAYOUT_VERSION,
})
// postinstall hooks

View File

@@ -12,7 +12,8 @@ import {
prune as pruneShrinkwrap,
} from 'pnpm-lockfile'
import {
save as saveModules
save as saveModules,
LAYOUT_VERSION,
} from '../fs/modulesController'
import removeOrphanPkgs from './removeOrphanPkgs'
import {PackageSpec} from '../resolve'
@@ -58,6 +59,7 @@ export async function uninstallInContext (pkgsToUninstall: string[], ctx: PnpmCo
packageManager: `${pnpmPkgJson.name}@${pnpmPkgJson.version}`,
storePath: ctx.storePath,
skipped: Array.from(ctx.skipped).filter(pkgId => removedPkgIds.indexOf(pkgId) === -1),
layoutVersion: LAYOUT_VERSION,
})
await removeOuterLinks(pkgsToUninstall, path.join(ctx.root, 'node_modules'), {storePath: ctx.storePath})
}

View File

@@ -6,10 +6,13 @@ import writeYamlFile = require('write-yaml-file')
// thinks that it is an extraneous package.
const modulesFileName = '.modules.yaml'
export const LAYOUT_VERSION = 1
export type Modules = {
packageManager: string,
storePath: string,
skipped: string[],
layoutVersion: number,
}
export async function read (modulesPath: string): Promise<Modules | null> {

View File

@@ -50,20 +50,6 @@ test('fail on non-compatible node_modules when forced with a named installation'
}
})
test('fail on non-compatible store', async t => {
const project = prepare(t)
const opts = testDefaults()
await saveModulesYaml('0.32.0', path.join(opts.store, STORE_VERSION))
try {
await installPkgs(['is-negative'], opts)
t.fail('should have failed')
} catch (err) {
t.equal(err.code, 'STORE_BREAKING_CHANGE', 'store breaking change error is thrown')
}
})
test("don't fail on non-compatible store when forced", async t => {
const project = prepare(t)
const opts = testDefaults({force: true})