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
20 changes: 17 additions & 3 deletions __tests__/commands/_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,23 @@ export function explodeLockfile(lockfile: string): Array<string> {
}

export async function getPackageVersion(config: Config, packagePath: string): Promise<string> {
const loc = path.join(config.cwd, `node_modules/${packagePath.replace(/\//g, '/node_modules/')}/package.json`);
const json = JSON.parse(await fs.readFile(loc));
return json.version;
return (await getPackageManifest(config, packagePath)).version;
}

export function getPackageManifest(config: Config, packagePath: string): Promise<any> {
return fs.readJson(getPackageManifestPath(config, packagePath));
}

export function getPackageManifestPath(config: Config, packagePath: string): string {
return path.join(getPackagePath(config, packagePath), 'package.json');
}

export function isPackagePresent(config: Config, packagePath: string): Promise<boolean> {
return fs.exists(getPackagePath(config, packagePath));
}

export function getPackagePath(config: Config, packagePath: string): string {
return path.join(config.cwd, `node_modules/${packagePath.replace(/\//g, '/node_modules/')}`);
}

export function makeConfigFromDirectory(cwd: string, reporter: Reporter, flags: Object = {}): Promise<Config> {
Expand Down
10 changes: 5 additions & 5 deletions __tests__/commands/install/integration-deduping.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @flow */

import {getPackageVersion, runInstall} from '../_helpers.js';
import {getPackageVersion, getPackageManifestPath, runInstall} from '../_helpers.js';
import * as fs from '../../../src/util/fs.js';

const path = require('path');
Expand Down Expand Up @@ -204,8 +204,8 @@ test.concurrent('install should hardlink repeated dependencies', (): Promise<voi
// B@1 -> A@2
// C@1 -> A@2 (this is hardlink to B@1->A@2)
return runInstall({linkDuplicates: true}, 'hardlink-repeated-dependencies', async config => {
const b_a = await fs.stat(path.join(config.cwd, 'node_modules/b/node_modules/a/package.json'));
const c_a = await fs.stat(path.join(config.cwd, 'node_modules/c/node_modules/a/package.json'));
const b_a = await fs.stat(getPackageManifestPath(config, 'b/a'));
const c_a = await fs.stat(getPackageManifestPath(config, 'c/a'));
expect(b_a.ino).toEqual(c_a.ino);
});
});
Expand All @@ -215,8 +215,8 @@ test.concurrent('install should not hardlink repeated dependencies if linkDuplic
// B@1 -> A@2
// C@1 -> A@2
return runInstall({linkDuplicates: false}, 'hardlink-repeated-dependencies', async config => {
const b_a = await fs.stat(path.join(config.cwd, 'node_modules/b/node_modules/a/package.json'));
const c_a = await fs.stat(path.join(config.cwd, 'node_modules/c/node_modules/a/package.json'));
const b_a = await fs.stat(getPackageManifestPath(config, 'b/a'));
const c_a = await fs.stat(getPackageManifestPath(config, 'c/a'));
expect(b_a.ino).not.toEqual(c_a.ino);
});
});
Expand Down
9 changes: 3 additions & 6 deletions __tests__/commands/install/integration-hoisting.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
/* @flow */

import {getPackageVersion, runInstall} from '../_helpers.js';
import * as fs from '../../../src/util/fs.js';

const path = require('path');
import {getPackageVersion, isPackagePresent, runInstall} from '../_helpers.js';

jasmine.DEFAULT_TIMEOUT_INTERVAL = 120000;

Expand All @@ -21,8 +18,8 @@ test.concurrent(
'install hoister should not install prioritised popular transitive devDependencies in --prod mode',
(): Promise<void> => {
return runInstall({production: true}, 'install-prod-prioritized-popular-transitive-dev-dep', async config => {
expect(await fs.exists(path.join(config.cwd, 'node_modules', 'a'))).toEqual(false);
expect(await fs.exists(path.join(config.cwd, 'node_modules', 'b'))).toEqual(false);
expect(await isPackagePresent(config, 'a')).toEqual(false);
expect(await isPackagePresent(config, 'b')).toEqual(false);
});
},
);
4 changes: 2 additions & 2 deletions __tests__/commands/install/lockfiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as reporters from '../../../src/reporters/index.js';
import {Install} from '../../../src/cli/commands/install.js';
import Lockfile from '../../../src/lockfile/wrapper.js';
import * as fs from '../../../src/util/fs.js';
import {getPackageVersion, runInstall} from '../_helpers.js';
import {getPackageVersion, isPackagePresent, runInstall} from '../_helpers.js';
import {promisify} from '../../../src/util/promise';

jasmine.DEFAULT_TIMEOUT_INTERVAL = 150000;
Expand Down Expand Up @@ -152,7 +152,7 @@ test.concurrent('install have a clean node_modules after lockfile update (branch
await reinstall.init();

expect(await getPackageVersion(config, 'dep-a')).toEqual('1.2.0');
expect(await fs.exists(path.join(config.cwd, 'node_modules/dep-b'))).toEqual(false);
expect(await isPackagePresent(config, 'dep-b')).toEqual(false);
});
});

Expand Down
26 changes: 26 additions & 0 deletions __tests__/commands/install/resolutions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* @flow */

import {getPackageVersion, isPackagePresent, runInstall} from '../_helpers.js';

test.concurrent('install with simple exact resolutions should override all versions', (): Promise<void> => {
return runInstall({}, {source: 'resolutions', cwd: 'simple-exact'}, async config => {
expect(await getPackageVersion(config, 'a')).toEqual('1.0.0');
expect(await getPackageVersion(config, 'b')).toEqual('1.0.0');
expect(await getPackageVersion(config, 'd1')).toEqual('2.0.0');
expect(await getPackageVersion(config, 'd2')).toEqual('1.0.0');
expect(await isPackagePresent(config, 'a/d1')).toEqual(false);
expect(await isPackagePresent(config, 'a/d2')).toEqual(false);
expect(await isPackagePresent(config, 'b/d1')).toEqual(false);
expect(await isPackagePresent(config, 'b/d2')).toEqual(false);
});
});

test.concurrent('install with subtree exact resolutions should override subtree versions', (): Promise<void> => {
return runInstall({}, {source: 'resolutions', cwd: 'subtree-exact'}, async config => {
expect(await getPackageVersion(config, 'left-pad')).toEqual('1.0.0');
expect(await getPackageVersion(config, 'd2')).toEqual('1.0.0');
expect(await getPackageVersion(config, 'd2/left-pad')).toEqual('1.1.1');
expect(await getPackageVersion(config, 'c')).toEqual('1.0.0');
expect(await getPackageVersion(config, 'c/left-pad')).toEqual('1.1.2');
});
});
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/a-1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "a",
"version": "1.0.0",
"dependencies": {
"d1": "file:../d1-1"
}
}
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/a-2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "a",
"version": "2.0.0",
"dependencies": {
"d1": "file:../d1-2"
}
}
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/b-1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "b",
"version": "1.0.0",
"dependencies": {
"d1": "file:../d1-2"
}
}
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/c-1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "c",
"version": "1.0.0",
"dependencies": {
"left-pad": "~1.1.1"
}
}
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/d1-1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "d1",
"version": "1.0.0",
"dependencies": {
"d2": "file:../d2-1"
}
}
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/d1-2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "d1",
"version": "2.0.0",
"dependencies": {
"d2": "file:../d2-1"
}
}
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/d1-3/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "d1",
"version": "3.0.0",
"dependencies": {
"d2": "file:../d2-1"
}
}
7 changes: 7 additions & 0 deletions __tests__/fixtures/install/resolutions/d2-1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "d2",
"version": "1.0.0",
"dependencies": {
"left-pad": "^1.0.0"
}
}
11 changes: 11 additions & 0 deletions __tests__/fixtures/install/resolutions/simple-exact/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "project",
"version": "1.0.0",
"dependencies": {
"a": "file:../a-1",
"b": "file:../b-1"
},
"resolutions": {
"**/d1": "file:../d1-2"
}
}
13 changes: 13 additions & 0 deletions __tests__/fixtures/install/resolutions/subtree-exact/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "project",
"version": "1.0.0",
"dependencies": {
"left-pad": "1.0.0",
"c": "file:../c-1",
"d2": "file:../d2-1"
},
"resolutions": {
"d2/left-pad": "1.1.1",
"c/**/left-pad": "1.1.2"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"gulp-util": "^3.0.7",
"gulp-watch": "^4.3.5",
"jest": "20.0.4",
"minimatch": "^3.0.4",
"mock-stdin": "^0.3.0",
"prettier": "^1.5.2",
"temp": "^0.8.3",
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ class ImportPackageRequest extends PackageRequest {
}

getParentHumanName(): string {
return [this.getRootName()].concat(this.getParentNames()).join(' > ');
return [this.getRootName()].concat(this.parentNames).join(' > ');
}

reportResolvedRangeMatch(info: Manifest, resolved: Manifest) {
Expand Down
18 changes: 14 additions & 4 deletions src/cli/commands/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import * as fs from '../../util/fs.js';
import map from '../../util/map.js';
import {version as YARN_VERSION, getInstallationMethod} from '../../util/yarn-version.js';
import WorkspaceLayout from '../../workspace-layout.js';
import ResolutionMap from '../../resolution-map.js';

const emoji = require('node-emoji');
const invariant = require('invariant');
Expand Down Expand Up @@ -165,13 +166,13 @@ export class Install {
constructor(flags: Object, config: Config, reporter: Reporter, lockfile: Lockfile) {
this.rootManifestRegistries = [];
this.rootPatternsToOrigin = map();
this.resolutions = map();
this.lockfile = lockfile;
this.reporter = reporter;
this.config = config;
this.flags = normalizeFlags(config, flags);

this.resolver = new PackageResolver(config, lockfile);
this.resolutions = map(); // Legacy resolutions field used for flat install mode
this.resolutionMap = new ResolutionMap(config); // Selective resolutions for nested dependencies
this.resolver = new PackageResolver(config, lockfile, this.resolutionMap);
this.integrityChecker = new InstallationIntegrityChecker(config);
this.linker = new PackageLinker(config, this.resolver);
this.scripts = new PackageInstallScripts(config, this.resolver, this.flags.force);
Expand All @@ -189,6 +190,7 @@ export class Install {
linker: PackageLinker;
rootPatternsToOrigin: {[pattern: string]: string};
integrityChecker: InstallationIntegrityChecker;
resolutionMap: ResolutionMap;

/**
* Create a list of dependency requests from the current directories manifests.
Expand All @@ -200,6 +202,7 @@ export class Install {
): Promise<InstallCwdRequest> {
const patterns = [];
const deps: DependencyRequestPatterns = [];
let resolutionDeps: DependencyRequestPatterns = [];
const manifest = {};

const ignorePatterns = [];
Expand Down Expand Up @@ -234,6 +237,13 @@ export class Install {
Object.assign(this.resolutions, projectManifestJson.resolutions);
Object.assign(manifest, projectManifestJson);

this.resolutionMap.init(this.resolutions);
for (const packageName of Object.keys(this.resolutionMap.resolutionsByPackage)) {
for (const {pattern} of this.resolutionMap.resolutionsByPackage[packageName]) {
resolutionDeps = [...resolutionDeps, {registry, pattern, optional: false, hint: 'resolution'}];
}
}

const pushDeps = (depType, manifest: Object, {hint, optional}, isUsed) => {
if (ignoreUnusedPatterns && !isUsed) {
return;
Expand Down Expand Up @@ -308,7 +318,7 @@ export class Install {
}

return {
requests: deps,
requests: [...resolutionDeps, ...deps],
patterns,
manifest,
usedPatterns,
Expand Down
24 changes: 7 additions & 17 deletions src/package-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type ResolverRegistryNames = $Keys<typeof registryResolvers>;
export default class PackageRequest {
constructor(req: DependencyRequestPattern, resolver: PackageResolver) {
this.parentRequest = req.parentRequest;
this.parentNames = [];
this.lockfile = resolver.lockfile;
this.registry = req.registry;
this.reporter = resolver.reporter;
Expand All @@ -38,6 +39,7 @@ export default class PackageRequest {
}

parentRequest: ?PackageRequest;
parentNames: Array<string>;
lockfile: Lockfile;
reporter: Reporter;
resolver: PackageResolver;
Expand All @@ -47,20 +49,6 @@ export default class PackageRequest {
optional: boolean;
foundInfo: ?Manifest;

getParentNames(): Array<string> {
const chain = [];

let request = this.parentRequest;
while (request) {
const info = this.resolver.getStrictResolvedPattern(request.pattern);
chain.unshift(info.name);

request = request.parentRequest;
}

return chain;
}

getLocked(remoteType: string): ?Object {
// always prioritise root lockfile
const shrunk = this.lockfile.getLocked(this.pattern);
Expand Down Expand Up @@ -110,7 +98,6 @@ export default class PackageRequest {
// "foo": "http://foo.com/bar.tar.gz"
// then we use the foo name
data.name = name;

return data;
}

Expand Down Expand Up @@ -267,6 +254,7 @@ export default class PackageRequest {
!info.fresh || frozen
? this.resolver.getExactVersionMatch(name, solvedRange, info)
: this.resolver.getHighestRangeVersionMatch(name, solvedRange, info);

if (resolved) {
this.resolver.reportPackageWithExistingVersion(this, info);
return;
Expand All @@ -290,11 +278,10 @@ export default class PackageRequest {
ref.setFresh(fresh);
info._reference = ref;
info._remote = remote;

// start installation of dependencies
const promises = [];
const deps = [];

const parentNames = [...this.parentNames, name];
// normal deps
for (const depName in info.dependencies) {
const depPattern = depName + '@' + info.dependencies[depName];
Expand All @@ -306,6 +293,7 @@ export default class PackageRequest {
// dependencies of optional dependencies should themselves be optional
optional: this.optional,
parentRequest: this,
parentNames,
}),
);
}
Expand All @@ -320,6 +308,7 @@ export default class PackageRequest {
registry: remote.registry,
optional: true,
parentRequest: this,
parentNames,
}),
);
}
Expand All @@ -334,6 +323,7 @@ export default class PackageRequest {
registry: remote.registry,
optional: false,
parentRequest: this,
parentNames,
}),
);
}
Expand Down
Loading