Skip to content

Commit

Permalink
Implements --no-default-rc and --use-rc (#6007)
Browse files Browse the repository at this point in the history
  • Loading branch information
arcanis authored Jun 27, 2018
1 parent d492daa commit e946807
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 58 deletions.
2 changes: 2 additions & 0 deletions __tests__/commands/_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export function makeConfigFromDirectory(cwd: string, reporter: Reporter, flags:
production: flags.production,
updateChecksums: !!flags.updateChecksums,
focus: !!flags.focus,
enableDefaultRc: !flags.noDefaultRc,
extraneousYarnrcFiles: flags.useYarnrc,
},
reporter,
);
Expand Down
1 change: 1 addition & 0 deletions __tests__/fixtures/cache/custom-location/.yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cache-folder "./uses-default-yarnrc"
1 change: 1 addition & 0 deletions __tests__/fixtures/cache/custom-location/custom-yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cache-folder "./uses-custom-yarnrc"
1 change: 1 addition & 0 deletions __tests__/fixtures/cache/custom-location/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
20 changes: 20 additions & 0 deletions __tests__/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,26 @@ test('--cwd option', async () => {
expect(packageJson.dependencies['left-pad']).toBeDefined();
});

const customCacheCwd = `${__dirname}/fixtures/cache/custom-location`;

test('default rc', async (): Promise<void> => {
const [stdoutOutput] = await runYarn(['cache', 'dir'], {cwd: customCacheCwd});

expect(stdoutOutput).toMatch(/uses-default-yarnrc/);
});

test('--no-default-rc', async (): Promise<void> => {
const [stdoutOutput] = await runYarn(['cache', 'dir', '--no-default-rc'], {cwd: customCacheCwd});

expect(stdoutOutput).not.toMatch(/uses-default-yarnrc/);
});

test('--use-yarnrc', async (): Promise<void> => {
const [stdoutOutput] = await runYarn(['cache', 'dir', '--use-yarnrc', './custom-yarnrc'], {cwd: customCacheCwd});

expect(stdoutOutput).toMatch(/uses-custom-yarnrc/);
});

test('yarnrc arguments', async () => {
const cwd = await makeTemp();

Expand Down
16 changes: 16 additions & 0 deletions __tests__/rc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ test('resolve .yarnrc args and use --cwd if present', () => {
expect(args.indexOf('--foo') !== -1).toBe(true);
});

test("don't resolve .yarnrc args from the default locations when using --no-default-rc", () => {
const args = getRcArgs('install', ['--cwd', path.join(fixturesLoc, 'empty'), '--no-default-rc']);
expect(args.indexOf('--foo') !== -1).toBe(false);
});

test('resolve .yarnrc args from the extraneous locations when using --use-yarnrc', () => {
const args = getRcArgs('install', [
'--cwd',
path.join(fixturesLoc, 'empty'),
'--no-default-rc',
'--use-yarnrc',
path.join(fixturesLoc, 'empty', '.yarnrc'),
]);
expect(args.indexOf('--foo') !== -1).toBe(true);
});

test('resolve .yarnrc args and use process.cwd() if no --cwd present', () => {
const cwd = process.cwd();
process.chdir(path.join(fixturesLoc, 'empty'));
Expand Down
26 changes: 13 additions & 13 deletions __tests__/registries/npm-registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('request', () => {
function createRegistry(config: Object): Object {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);
npmRegistry.config = config;
return {
request(url: string, options: Object, packageName: string): Object {
Expand Down Expand Up @@ -577,7 +577,7 @@ describe('isRequestToRegistry functional test', () => {
test('request to registry url matching', () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

const validRegistryUrls = [
['http://foo.bar:80/foo/bar/baz', 'http://foo.bar/foo/'],
Expand Down Expand Up @@ -609,7 +609,7 @@ describe('isRequestToRegistry functional test', () => {
test('isRequestToRegistry with custom host prefix', () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

npmRegistry.config = {
'custom-host-suffix': 'some.host.org',
Expand Down Expand Up @@ -659,7 +659,7 @@ describe('isScopedPackage functional test', () => {
test('identifies scope correctly', () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

packageIdents.forEach(([pathname, scope]) => {
expect(npmRegistry.isScopedPackage(pathname)).toEqual(!!scope.length);
Expand All @@ -671,7 +671,7 @@ describe('getRequestUrl functional test', () => {
test('returns pathname when it is a full URL', () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);
const fullURL = 'HTTP://xn--xample-hva.com:80/foo/bar/baz';

expect(npmRegistry.getRequestUrl('https://my.registry.co', fullURL)).toEqual(fullURL);
Expand All @@ -680,7 +680,7 @@ describe('getRequestUrl functional test', () => {
test('correctly handles registries lacking a trailing slash', () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);
const registry = 'https://my.registry.co/registry';
const pathname = 'foo/bar/baz';

Expand All @@ -692,7 +692,7 @@ describe('getScope functional test', () => {
describe('matches scope correctly', () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

packageIdents.forEach(([pathname, scope]) => {
expect(npmRegistry.getScope(pathname)).toEqual(scope);
Expand All @@ -705,7 +705,7 @@ describe('getPossibleConfigLocations', () => {
const testCwd = './project/subdirectory';
const {mockRequestManager, mockRegistries} = createMocks();
const reporter = new BufferReporter({verbose: true});
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, reporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, reporter, true, []);
await npmRegistry.getPossibleConfigLocations('npmrc', reporter);

const logs = reporter.getBuffer().map(logItem => logItem.data);
Expand All @@ -730,7 +730,7 @@ describe('checkOutdated functional test', () => {
test('homepage URL from top level', async () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

mockRequestManager.request = () => {
return {
Expand Down Expand Up @@ -758,7 +758,7 @@ describe('checkOutdated functional test', () => {
test('homepage URL fallback to wanted package manifest', async () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

mockRequestManager.request = () => {
return {
Expand Down Expand Up @@ -786,7 +786,7 @@ describe('checkOutdated functional test', () => {
test('repository URL from top level', async () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

mockRequestManager.request = () => {
return {
Expand Down Expand Up @@ -816,7 +816,7 @@ describe('checkOutdated functional test', () => {
test('repository URL fallback to wanted package manifest', async () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

mockRequestManager.request = () => {
return {
Expand Down Expand Up @@ -846,7 +846,7 @@ describe('checkOutdated functional test', () => {
test('unpublished package (no versions)', async () => {
const testCwd = '.';
const {mockRequestManager, mockRegistries, mockReporter} = createMocks();
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter);
const npmRegistry = new NpmRegistry(testCwd, mockRegistries, mockRequestManager, mockReporter, true, []);

mockRequestManager.request = () => {
return {
Expand Down
5 changes: 4 additions & 1 deletion bin/yarn.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ if (majorVer < 4) {
// be truthy when built with webpack :(
var cli = require(dirPath + 'cli');
if (!cli.autoRun) {
cli.default();
cli.default().catch(function(error) {
console.error(error.stack || error.message || error);
process.exitCode = 1;
});
}
}
29 changes: 23 additions & 6 deletions src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,33 @@ function findProjectRoot(base: string): string {
return base;
}

export function main({
export async function main({
startArgs,
args,
endArgs,
}: {
startArgs: Array<string>,
args: Array<string>,
endArgs: Array<string>,
}) {
}): Promise<void> {
const collect = (val, acc) => {
acc.push(val);
return acc;
};

loudRejection();
handleSignals();

// set global options
commander.version(version, '-v, --version');
commander.usage('[command] [flags]');
commander.option('--no-default-rc', 'prevent Yarn from automatically detecting yarnrc and npmrc files');
commander.option(
'--use-yarnrc <path>',
'specifies a yarnrc file that Yarn should use (.yarnrc only, not .npmrc)',
collect,
[],
);
commander.option('--verbose', 'output verbose messages on internal operations');
commander.option('--offline', 'trigger an error if any required dependencies are not available in local cache');
commander.option('--prefer-offline', 'use network only if dependencies are not available in local cache');
Expand Down Expand Up @@ -477,11 +489,13 @@ export function main({

const cwd = command.shouldRunInCurrentCwd ? commander.cwd : findProjectRoot(commander.cwd);

config
await config
.init({
cwd,
commandName,

enableDefaultRc: commander.defaultRc,
extraneousYarnrcFiles: commander.useYarnrc,
binLinks: commander.binLinks,
modulesFolder: commander.modulesFolder,
linkFolder: commander.linkFolder,
Expand Down Expand Up @@ -564,7 +578,7 @@ export function main({
}

async function start(): Promise<void> {
const rc = getRcConfigForCwd(process.cwd());
const rc = getRcConfigForCwd(process.cwd(), process.argv.slice(2));
const yarnPath = rc['yarn-path'];

if (yarnPath && !boolifyWithDefault(process.env.YARN_IGNORE_PATH, false)) {
Expand All @@ -590,7 +604,7 @@ async function start(): Promise<void> {
const args = process.argv.slice(2, doubleDashIndex === -1 ? process.argv.length : doubleDashIndex);
const endArgs = doubleDashIndex === -1 ? [] : process.argv.slice(doubleDashIndex);

main({startArgs, args, endArgs});
await main({startArgs, args, endArgs});
}
}

Expand All @@ -599,7 +613,10 @@ async function start(): Promise<void> {
export const autoRun = module.children.length === 0;

if (require.main === module) {
start();
start().catch(error => {
console.error(error.stack || error.message || error);
process.exitCode = 1;
});
}

export default start;
21 changes: 20 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export type ConfigOptions = {
nonInteractive?: boolean,
scriptsPrependNodePath?: boolean,

enableDefaultRc?: boolean,
extraneousYarnrcFiles?: Array<string>,

// Loosely compare semver for invalid cases like "0.01.0"
looseSemver?: ?boolean,

Expand Down Expand Up @@ -96,6 +99,10 @@ export default class Config {
this._init({});
}

//
enableDefaultRc: boolean;
extraneousYarnrcFiles: Array<string>;

//
looseSemver: boolean;
offline: boolean;
Expand Down Expand Up @@ -272,8 +279,17 @@ export default class Config {
for (const key of Object.keys(registries)) {
const Registry = registries[key];

const extraneousRcFiles = Registry === registries.yarn ? this.extraneousYarnrcFiles : [];

// instantiate registry
const registry = new Registry(this.cwd, this.registries, this.requestManager, this.reporter);
const registry = new Registry(
this.cwd,
this.registries,
this.requestManager,
this.reporter,
this.enableDefaultRc,
extraneousRcFiles,
);
await registry.init({
registry: opts.registry,
});
Expand Down Expand Up @@ -388,6 +404,9 @@ export default class Config {

this.commandName = opts.commandName || '';

this.enableDefaultRc = !!opts.enableDefaultRc;
this.extraneousYarnrcFiles = opts.extraneousYarnrcFiles || [];

this.preferOffline = !!opts.preferOffline;
this.modulesFolder = opts.modulesFolder;
this.globalFolder = opts.globalFolder || constants.GLOBAL_MODULE_DIRECTORY;
Expand Down
50 changes: 36 additions & 14 deletions src/rc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* @flow */

import {readFileSync} from 'fs';
import {dirname, resolve} from 'path';

import commander from 'commander';
Expand All @@ -11,24 +12,45 @@ import * as rcUtil from './util/rc.js';
const PATH_KEYS = new Set(['yarn-path', 'cache-folder', 'global-folder', 'modules-folder', 'cwd']);

// given a cwd, load all .yarnrc files relative to it
export function getRcConfigForCwd(cwd: string): {[key: string]: string} {
return rcUtil.findRc('yarn', cwd, (fileText, filePath) => {
const {object: values} = parse(fileText, 'yarnrc');

// some keys reference directories so keep their relativity
for (const key in values) {
if (PATH_KEYS.has(key.replace(/^(--)?([^.]+\.)*/, ''))) {
values[key] = resolve(dirname(filePath), values[key]);
}
export function getRcConfigForCwd(cwd: string, args: Array<string>): {[key: string]: string} {
const config = {};

if (args.indexOf('--no-default-rc') === -1) {
Object.assign(
config,
rcUtil.findRc('yarn', cwd, (fileText, filePath) => {
return loadRcFile(fileText, filePath);
}),
);
}

for (let index = args.indexOf('--use-yarnrc'); index !== -1; index = args.indexOf('--use-yarnrc', index + 1)) {
const value = args[index + 1];

if (value && value.charAt(0) !== '-') {
Object.assign(config, loadRcFile(readFileSync(value).toString(), value));
}
}

return config;
}

function loadRcFile(fileText: string, filePath: string): {[key: string]: string} {
const {object: values} = parse(fileText, 'yarnrc');

// some keys reference directories so keep their relativity
for (const key in values) {
if (PATH_KEYS.has(key.replace(/^(--)?([^.]+\.)*/, ''))) {
values[key] = resolve(dirname(filePath), values[key]);
}
}

return values;
});
return values;
}

// get the built of arguments of a .yarnrc chain of the passed cwd
function buildRcArgs(cwd: string): Map<string, Array<string>> {
const config = getRcConfigForCwd(cwd);
function buildRcArgs(cwd: string, args: Array<string>): Map<string, Array<string>> {
const config = getRcConfigForCwd(cwd, args);

const argsForCommands: Map<string, Array<string>> = new Map();

Expand Down Expand Up @@ -82,7 +104,7 @@ export function getRcArgs(commandName: string, args: Array<string>, previousCwds
const origCwd = extractCwdArg(args) || process.cwd();

// get a map of command names and their arguments
const argMap = buildRcArgs(origCwd);
const argMap = buildRcArgs(origCwd, args);

// concat wildcard arguments and arguments meant for this specific command
const newArgs = [...(argMap.get('*') || []), ...(argMap.get(commandName) || [])];
Expand Down
Loading

0 comments on commit e946807

Please sign in to comment.