Skip to content

Commit

Permalink
Merge branch 'master' into update-check
Browse files Browse the repository at this point in the history
# Conflicts:
#	package.json
#	src/config.js
#	yarn.lock
  • Loading branch information
Sebastian McKenzie committed Nov 3, 2016
2 parents e864da9 + 8de54b1 commit 6e707f3
Show file tree
Hide file tree
Showing 176 changed files with 3,649 additions and 611 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ test/fixtures/**/.fbkpm

# IDE
.idea/
/__tests__/fixtures/request-cache/
20 changes: 9 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,26 @@

**Fast:** Yarn caches every package it downloads so it never needs to download the same package again. It also parallelizes operations to maximize resource utilization so install times are faster than ever.

**Reliable:** Using a detailed, but concise, lockfile format, and a deterministic algorithm for installs, Yarn is able to guarantee that an install that worked on one system will work exactly the same way on any other system.
**Reliable:** Using a detailed, concise lockfile format and a deterministic algorithm for installs, Yarn is able to guarantee that an install that worked on one system will work exactly the same way on any other system.

**Secure:** Yarn uses checksums to verify the integrity of every installed package before its code is executed.

## Features

* Offline Mode: If you've installed a package before, you can install it again without any internet connection.
* Deterministic: The same dependencies will be installed the same exact way across every machine regardless of install order.
* Network Performance: Yarn efficiently queues up requests and avoids request waterfalls in order to maximize network utilization.
* Multiple Registries: Install any package from either npm or Bower and keep your package workflow the same.
* Network Resilience: A single request failing won't cause an install to fail. Requests are retried upon failure.
* Flat Mode: Resolve mismatching versions of dependencies to a single version to avoid creating duplicates.
* More emojis. 🐈
* **Offline Mode.** If you've installed a package before, you can install it again without any internet connection.
* **Deterministic.** The same dependencies will be installed in the same exact way on any machine, regardless of install order.
* **Network Performance.** Yarn efficiently queues up requests and avoids request waterfalls in order to maximize network utilization.
* **Network Resilience.** A single request failing won't cause an install to fail. Requests are retried upon failure.
* **Flat Mode.** Yarn resolves mismatched versions of dependencies to a single version to avoid creating duplicates.
* **More emojis.** 🐈

## Installing Yarn

Read the [Installation Guide](https://yarnpkg.com/en/docs/install) on our website for detailed instructions on how to install Yarn on your operating system.
Read the [Installation Guide](https://yarnpkg.com/en/docs/install) on our website for detailed instructions on how to install Yarn.

## Contributing to Yarn

Contributions are always welcome, no matter how large or small. Before contributing,
please read the [code of conduct](CODE_OF_CONDUCT.md).
Contributions are always welcome, no matter how large or small. Before contributing, please read the [code of conduct](CODE_OF_CONDUCT.md).

See [Contributing](CONTRIBUTING.md).

Expand Down
2 changes: 1 addition & 1 deletion __tests__/__snapshots__/fetchers.js.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
exports[`test TarballFetcher.fetch throws 1`] = `"https://github.com/PolymerElements/font-roboto/archive/2fd5c7bd715a24fb5b250298a140a3ba1b71fe46.tar.gz: Bad hash. Expected foo but got 9689b3b48d63ff70f170a192bec3c01b04f58f45 "`;
exports[`test TarballFetcher.fetch throws on invalid hash 1`] = `"https://github.com/sindresorhus/beeper/archive/master.tar.gz: Bad hash. Expected \"foo\" but got \"a32262ca1e22a3746b970936d3944b4bfd6cb9e9\" "`;
32 changes: 32 additions & 0 deletions __tests__/cli/aliases.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* @flow */

import aliases from '../../src/cli/aliases.js';

test('shorthands and affordances', () => {
expect(aliases['run-script']).toBe('run');
expect(aliases['c']).toBe('config');
expect(aliases['i']).toBe('install');
expect(aliases['list']).toBe('ls');
expect(aliases['rb']).toBe('rebuild');
expect(aliases['runScript']).toBe('run');
expect(aliases['t']).toBe('test');
expect(aliases['tst']).toBe('test');
expect(aliases['un']).toBe('remove');
expect(aliases['up']).toBe('update');
expect(aliases['v']).toBe('version');
expect(aliases['add-user']).toBe('login');
expect(aliases['dist-tag']).toBe('tag');
expect(aliases['dist-tags']).toBe('tag');
expect(aliases['adduser']).toBe('login');
expect(aliases['author']).toBe('owner');
expect(aliases['isntall']).toBe('install');
expect(aliases['la']).toBe('ls');
expect(aliases['ll']).toBe('ls');
expect(aliases['r']).toBe('remove');
expect(aliases['rm']).toBe('remove');
expect(aliases['show']).toBe('info');
expect(aliases['uninstall']).toBe('remove');
expect(aliases['update']).toBe('upgrade');
expect(aliases['verison']).toBe('version');
expect(aliases['view']).toBe('info');
});
8 changes: 6 additions & 2 deletions __tests__/commands/_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ export function runInstall(
name: string,
checkInstalled?: ?(config: Config, reporter: Reporter, install: Install) => ?Promise<void>,
beforeInstall?: ?(cwd: string) => ?Promise<void>,
cleanupAfterInstall: boolean = true,
): Promise<void> {
return run((config, reporter, lockfile): Install => {
return new Install(flags, config, reporter, lockfile);
}, path.join(fixturesLoc, name), checkInstalled, beforeInstall);
}, path.join(fixturesLoc, name), checkInstalled, beforeInstall, cleanupAfterInstall);
}

export async function createLockfile(dir: string): Promise<Lockfile> {
Expand Down Expand Up @@ -54,6 +55,7 @@ export async function run(
dir: string,
checkInstalled: ?(config: Config, reporter: Reporter, install: Install) => ?Promise<void>,
beforeInstall: ?(cwd: string) => ?Promise<void>,
cleanupAfterInstall: boolean,
): Promise<void> {
const cwd = path.join(
os.tmpdir(),
Expand Down Expand Up @@ -110,7 +112,9 @@ export async function run(
}
} finally {
// clean up
await fs.unlink(cwd);
if (cleanupAfterInstall) {
await fs.unlink(cwd);
}
}
} catch (err) {
throw new Error(`${err && err.stack} \nConsole output:\n ${out}`);
Expand Down
3 changes: 2 additions & 1 deletion __tests__/commands/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ function runAdd(
name: string,
checkInstalled?: ?(config: Config, reporter: Reporter) => ?Promise<void>,
beforeInstall?: ?(cwd: string) => ?Promise<void>,
cleanupAfterInstall: boolean = true,
): Promise<void> {
return buildRun((config, reporter, lockfile): Install => {
return new Add(args, flags, config, reporter, lockfile);
}, path.join(fixturesLoc, name), checkInstalled, beforeInstall);
}, path.join(fixturesLoc, name), checkInstalled, beforeInstall, cleanupAfterInstall);
}

test.concurrent('install with arg that has install scripts', (): Promise<void> => {
Expand Down
9 changes: 9 additions & 0 deletions __tests__/commands/init.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* @flow */

import {runInit} from './_init.js';
import {getGitConfigInfo} from '../../src/cli/commands/init.js';
import * as fs from '../../src/util/fs.js';
import assert from 'assert';

Expand All @@ -20,3 +21,11 @@ test.concurrent('init --yes should create package.json with defaults', (): Prom
assert.equal(manifest.license, String(config.getOption('init-license')));
});
});

test.concurrent('getGitConfigInfo should not return the git config val', async (): Promise<void> => {
assert.equal('hi seb', await getGitConfigInfo('some-info', () => Promise.resolve('hi seb')));
});

test.concurrent('getGitConfigInfo should not fail when git fails', async (): Promise<void> => {
assert.equal('', await getGitConfigInfo('some-info', () => Promise.reject(Error())));
});
156 changes: 48 additions & 108 deletions __tests__/commands/install.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* @flow */

import {run as uninstall} from '../../src/cli/commands/remove.js';
import {run as check} from '../../src/cli/commands/check.js';
import * as reporters from '../../src/reporters/index.js';
import {Install} from '../../src/cli/commands/install.js';
Expand Down Expand Up @@ -114,6 +113,10 @@ test.concurrent('install from offline mirror', (): Promise<void> => {
assert(allFiles.findIndex((file): boolean => {
return file.relative === path.join('node_modules', 'fake-dependency', 'package.json');
}) !== -1);

assert(allFiles.findIndex((file): boolean => {
return file.relative === path.join('node_modules', '@fakescope', 'fake-dependency', 'package.json');
}) !== -1);
});
});

Expand Down Expand Up @@ -290,30 +293,27 @@ test.concurrent('install should dedupe dependencies avoiding conflicts 7', (): P

test.concurrent('install should dedupe dependencies avoiding conflicts 8', (): Promise<void> => {
// revealed in https://github.com/yarnpkg/yarn/issues/112
// adapted for https://github.com/yarnpkg/yarn/issues/1158
return runInstall({}, 'install-should-dedupe-avoiding-conflicts-8', async (config) => {
assert.equal(await getPackageVersion(config, 'glob'), '5.0.15');
assert.equal(await getPackageVersion(config, 'yeoman-generator/globby/glob'), '6.0.4');
assert.equal(await getPackageVersion(config, 'findup-sync/glob'), '4.3.5');
assert.equal(await getPackageVersion(config, 'inquirer'), '0.8.5');
assert.equal(await getPackageVersion(config, 'yeoman-generator/yeoman-environment/inquirer'), '1.1.3');
assert.equal(await getPackageVersion(config, 'lodash'), '3.10.1');
assert.equal(await getPackageVersion(config, 'yeoman-generator/yeoman-environment/lodash'), '4.15.0');
assert.equal(await getPackageVersion(config, 'ast-query/lodash'), '4.15.0');
assert.equal(await getPackageVersion(config, 'run-async'), '0.1.0');
assert.equal(await getPackageVersion(config, 'yeoman-generator/yeoman-environment/run-async'), '2.2.0');
});
});


test.concurrent('install should dedupe dependencies avoiding conflicts 9', (): Promise<void> => {
// revealed in https://github.com/yarnpkg/yarn/issues/112
// adapted for https://github.com/yarnpkg/yarn/issues/1158
return runInstall({}, 'install-should-dedupe-avoiding-conflicts-9', async (config) => {
assert.equal(await getPackageVersion(config, 'glob'), '5.0.15');
assert.equal(await getPackageVersion(config, 'yeoman-generator/globby/glob'), '6.0.4');
assert.equal(await getPackageVersion(config, 'findup-sync/glob'), '4.3.5');
assert.equal(await getPackageVersion(config, 'inquirer'), '0.8.5');
assert.equal(await getPackageVersion(config, 'yeoman-generator/yeoman-environment/inquirer'), '1.1.3');
assert.equal(await getPackageVersion(config, 'lodash'), '3.10.1');
assert.equal(await getPackageVersion(config, 'yeoman-generator/yeoman-environment/lodash'), '4.15.0');
assert.equal(await getPackageVersion(config, 'ast-query/lodash'), '4.15.0');
assert.equal(await getPackageVersion(config, 'run-async'), '0.1.0');
assert.equal(await getPackageVersion(config, 'yeoman-generator/yeoman-environment/run-async'), '2.2.0');
});
});

Expand Down Expand Up @@ -388,104 +388,6 @@ test.concurrent(
},
);

test.concurrent(
'uninstall should remove dependency from package.json, yarn.lock and node_modules',
(): Promise<void> => {
const mirrorPath = 'mirror-for-offline';

return runInstall({}, 'uninstall-should-clean', async (config, reporter) => {
assert.equal(
await getPackageVersion(config, 'dep-a'),
'1.0.0',
);

await fs.copy(path.join(config.cwd, 'yarn.lock'), path.join(config.cwd, 'yarn.lock.orig'));
await fs.copy(path.join(config.cwd, 'package.json'), path.join(config.cwd, 'package.json.orig'));

try {
await uninstall(config, reporter, {}, ['dep-a']);

assert(!await fs.exists(path.join(config.cwd, 'node_modules/dep-a')));
assert(await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-a-1.0.0.tgz`)));

assert.deepEqual(
JSON.parse(await fs.readFile(path.join(config.cwd, 'package.json'))).dependencies,
{},
);

const lockFileContent = await fs.readFile(path.join(config.cwd, 'yarn.lock'));
const lockFileLines = explodeLockfile(lockFileContent);
assert.equal(lockFileLines.length, 0);
} finally {
await fs.unlink(path.join(config.cwd, 'yarn.lock'));
await fs.unlink(path.join(config.cwd, 'package.json'));
await fs.copy(path.join(config.cwd, 'yarn.lock.orig'), path.join(config.cwd, 'yarn.lock'));
await fs.copy(path.join(config.cwd, 'package.json.orig'), path.join(config.cwd, 'package.json'));
await fs.unlink(path.join(config.cwd, 'yarn.lock.orig'));
await fs.unlink(path.join(config.cwd, 'package.json.orig'));
}
});
},
);

test.concurrent('uninstall should remove subdependencies', (): Promise<void> => {
// A@1 -> B@1
// C@1

// remove A

// C@1

const mirrorPath = 'mirror-for-offline';

return runInstall({}, 'uninstall-should-remove-subdependencies', async (config, reporter) => {
try {
assert.equal(
await getPackageVersion(config, 'dep-a'),
'1.0.0',
);
assert.equal(
await getPackageVersion(config, 'dep-b'),
'1.0.0',
);
assert.equal(
await getPackageVersion(config, 'dep-c'),
'1.0.0',
);

await fs.copy(path.join(config.cwd, 'yarn.lock'), path.join(config.cwd, 'yarn.lock.orig'));
await fs.copy(path.join(config.cwd, 'package.json'), path.join(config.cwd, 'package.json.orig'));

await uninstall(config, reporter, {}, ['dep-a']);

assert(!await fs.exists(path.join(config.cwd, 'node_modules/dep-a')));
assert(!await fs.exists(path.join(config.cwd, 'node_modules/dep-b')));
assert(await fs.exists(path.join(config.cwd, 'node_modules/dep-c')));

assert(await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-a-1.0.0.tgz`)));
assert(await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-b-1.0.0.tgz`)));
assert(await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-c-1.0.0.tgz`)));

assert.deepEqual(
JSON.parse(await fs.readFile(path.join(config.cwd, 'package.json'))).dependencies,
{'dep-c': '^1.0.0'},
);

const lockFileContent = await fs.readFile(path.join(config.cwd, 'yarn.lock'));
const lockFileLines = explodeLockfile(lockFileContent);
assert.equal(lockFileLines.length, 3);
assert.equal(lockFileLines[0], 'dep-c@^1.0.0:');
} finally {
await fs.unlink(path.join(config.cwd, 'yarn.lock'));
await fs.unlink(path.join(config.cwd, 'package.json'));
await fs.copy(path.join(config.cwd, 'yarn.lock.orig'), path.join(config.cwd, 'yarn.lock'));
await fs.copy(path.join(config.cwd, 'package.json.orig'), path.join(config.cwd, 'package.json'));
await fs.unlink(path.join(config.cwd, 'yarn.lock.orig'));
await fs.unlink(path.join(config.cwd, 'package.json.orig'));
}
});
});

test.concurrent('check should verify that top level dependencies are installed correctly', (): Promise<void> => {
return runInstall({}, 'check-top-correct', async (config, reporter) => {

Expand Down Expand Up @@ -556,6 +458,44 @@ test.concurrent('install should resolve circular dependencies 2', (): Promise<vo
});
});


test('install should respect NODE_ENV=production', (): Promise<void> => {
const env = process.env.NODE_ENV;
process.env.NODE_ENV = 'production';
return runInstall({}, 'install-should-respect-node_env', async (config) => {
expect(await fs.exists(path.join(config.cwd, 'node_modules/is-negative-zero/package.json'))).toBe(false);
// restore env
process.env.NODE_ENV = env;
});
});


test.concurrent('install should resolve circular dependencies 2', (): Promise<void> => {
return runInstall({}, 'install-should-circumvent-circular-dependencies-2', async (config, reporter) => {
assert.equal(
await getPackageVersion(config, 'es5-ext'),
'0.10.12',
);
});
});

test.concurrent('install should be idempotent', (): Promise<void> => {
// Install a package twice
runInstall({}, 'install-should-be-idempotent', async (config, reporter) => {
assert.equal(
await getPackageVersion(config, 'dep-a'),
'1.0.0',
);
}, null, false);

return runInstall({}, 'install-should-be-idempotent', async (config, reporter) => {
assert.equal(
await getPackageVersion(config, 'dep-a'),
'1.0.0',
);
});
});

test.concurrent(
'install should add missing deps to yarn and mirror (PR import scenario)',
(): Promise<void> => {
Expand Down

0 comments on commit 6e707f3

Please sign in to comment.