Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(linking): Handle missing package when using --ignore-optional #5059

Merged
merged 8 commits into from Mar 7, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 18 additions & 11 deletions __tests__/commands/install/integration.js
Expand Up @@ -811,17 +811,24 @@ test.concurrent('a subdependency of an optional dependency that fails should be
});
});

test.concurrent('a sub-dependency should be non-optional if any parents mark it non-optional', async (): Promise<
void,
> => {
let thrown = false;
try {
await runInstall({}, 'failing-sub-dep-optional-and-normal', () => {});
} catch (err) {
thrown = true;
expect(err.message).toContain('sub-failing: Command failed');
}
expect(thrown).toEqual(true);
test.concurrent('a sub-dependency should be non-optional if any parents mark it non-optional', (): Promise<void> => {
return runInstall(
{ignoreOptional: true},
'install-sub-dependency-if-any-parents-mark-it-non-optional',
async config => {
const deps = await fs.readdir(path.join(config.cwd, 'node_modules'));

expect(deps).toEqual([
'.yarn-integrity',
'normal-dep',
'normal-sub-dep',
'normal-sub-sub-dep',
'sub-dep',
'sub-dep-2',
'sub-sub-dep',
]);
},
);
});

test.concurrent('should not loose dependencies when installing with --production', (): Promise<void> => {
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -0,0 +1,11 @@
{
"name": "normal-dep",
"version": "0.0.0",
"dependencies": {
"sub-dep": "file:../sub-dep",
"normal-sub-dep": "file:../normal-sub-dep"
},
"optionalDependencies": {
"optional-sub-dep": "file:../optional-sub-dep"
}
}
@@ -0,0 +1,7 @@
{
"name": "normal-sub-dep",
"version": "0.0.0",
"dependencies": {
"normal-sub-sub-dep": "file:../normal-sub-sub-dep"
}
}
@@ -0,0 +1,7 @@
{
"name": "normal-sub-sub-dep",
"version": "0.0.0",
"dependencies": {
"sub-dep-2": "file:../sub-dep-2"
}
}
@@ -0,0 +1,8 @@
{
"name": "optional-dep",
"version": "0.0.0",
"dependencies": {
"sub-dep": "file:../sub-dep",
"sub-dep-2": "file:../sub-dep-2"
}
}
@@ -0,0 +1,7 @@
{
"name": "optional-sub-dep",
"version": "0.0.0",
"dependencies": {
"normal-sub-sub-dep": "file:../normal-sub-sub-dep"
}
}
@@ -0,0 +1,7 @@
{
"name": "sub-dep-2",
"version": "0.0.0",
"dependencies": {
"sub-sub-dep": "file:../sub-sub-dep"
}
}
@@ -0,0 +1,4 @@
{
"name": "sub-dep",
"version": "0.0.0"
}
@@ -0,0 +1,4 @@
{
"name": "sub-sub-dep",
"version": "0.0.0"
}
7 changes: 6 additions & 1 deletion src/cli/commands/install.js
Expand Up @@ -282,7 +282,12 @@ export class Install {
}
}

const pushDeps = (depType, manifest: Object, {hint, optional}, isUsed) => {
const pushDeps = (
depType,
manifest: Object,
{hint, optional}: {hint: ?constants.RequestHint, optional: boolean},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make hint optional here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of

pushDeps('dependencies', projectManifestJson, {hint: null, optional: false}, true);

Not sure if constants.RequestHint | null or ?constants.RequestHint is preferred(?) Or maybe add a production hint and always require hint?

isUsed,
) => {
if (ignoreUnusedPatterns && !isUsed) {
return;
}
Expand Down
2 changes: 2 additions & 0 deletions src/constants.js
Expand Up @@ -124,3 +124,5 @@ export const VERSION_COLOR_SCHEME: {[key: string]: VersionColor} = {
};

export type VersionColor = 'red' | 'yellow' | 'green' | 'white';

export type RequestHint = 'dev' | 'optional' | 'resolution' | 'workspaces';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't see other hints being used anywhere else. Do we need anything other than 'dev' and 'optional' at the moment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolution is used in:

resolutionDeps = [...resolutionDeps, {registry, pattern, optional: false, hint: 'resolution'}];

and workspaces in:

pushDeps('workspaces', {workspaces: virtualDep}, {hint: 'workspaces', optional: false}, true);

9 changes: 8 additions & 1 deletion src/package-hoister.js
Expand Up @@ -262,7 +262,14 @@ export default class PackageHoister {
continue;
}

const isMarkedAsOptional = depinfo.pkg._reference && depinfo.pkg._reference.optional && this.ignoreOptional;
const depRef = depinfo.pkg._reference;

// If it's marked as optional, but the parent is required and the
// dependency was not listed in `optionalDependencies`, then we mark the
// dependency as required.
const isMarkedAsOptional =
depRef && depRef.optional && this.ignoreOptional && !(info.isRequired && depRef.hint !== 'optional');

if (!depinfo.isRequired && !depinfo.isIncompatible && !isMarkedAsOptional) {
depinfo.isRequired = true;
depinfo.addHistory(`Mark as non-ignored because of usage by ${info.key}`);
Expand Down
3 changes: 3 additions & 0 deletions src/package-reference.js
Expand Up @@ -7,13 +7,15 @@ import type PackageRequest from './package-request.js';
import type PackageResolver from './package-resolver.js';
import type {RegistryNames} from './registries/index.js';
import {entries} from './util/misc.js';
import type {RequestHint} from './constants';

export default class PackageReference {
constructor(request: PackageRequest, info: Manifest, remote: PackageRemote) {
this.resolver = request.resolver;
this.lockfile = request.lockfile;
this.requests = [];
this.config = request.config;
this.hint = request.hint;

this.registry = remote.registry;
this.version = info.version;
Expand Down Expand Up @@ -44,6 +46,7 @@ export default class PackageReference {
version: string;
uid: string;
optional: ?boolean;
hint: ?RequestHint;
ignore: boolean;
incompatible: boolean;
fresh: boolean;
Expand Down
4 changes: 4 additions & 0 deletions src/package-request.js
Expand Up @@ -37,6 +37,7 @@ export default class PackageRequest {
this.reporter = resolver.reporter;
this.resolver = resolver;
this.optional = req.optional;
this.hint = req.hint;
this.pattern = req.pattern;
this.config = resolver.config;
this.foundInfo = null;
Expand All @@ -55,6 +56,7 @@ export default class PackageRequest {
config: Config;
registry: ResolverRegistryNames;
optional: boolean;
hint: ?constants.RequestHint;
foundInfo: ?Manifest;

getLocked(remoteType: FetcherNames): ?Manifest {
Expand Down Expand Up @@ -288,6 +290,7 @@ export default class PackageRequest {
deps.push(depPattern);
promises.push(
this.resolver.find({
hint: 'optional',
pattern: depPattern,
registry: remote.registry,
optional: true,
Expand All @@ -303,6 +306,7 @@ export default class PackageRequest {
deps.push(depPattern);
promises.push(
this.resolver.find({
hint: 'dev',
pattern: depPattern,
registry: remote.registry,
optional: false,
Expand Down
3 changes: 2 additions & 1 deletion src/types.js
Expand Up @@ -6,6 +6,7 @@ import type PackageRequest from './package-request.js';
import type {FetcherNames} from './fetchers/index.js';
import type {Reporter} from './reporters/index.js';
import type Config from './config.js';
import type {RequestHint} from './constants';

export type CLIFunction = (config: Config, reporter: Reporter, flags: Object, args: Array<string>) => CLIFunctionReturn;

Expand All @@ -18,7 +19,7 @@ export type DependencyRequestPattern = {
pattern: string,
registry: RegistryNames,
optional: boolean,
hint?: ?string,
hint?: ?RequestHint,
parentNames?: Array<string>,
parentRequest?: ?PackageRequest,
workspaceName?: string,
Expand Down