Skip to content

Commit

Permalink
[feature] Ability to pass command CLI arguments via configuration in …
Browse files Browse the repository at this point in the history
….yarnrc (#3033)

This PR opens the way to share configuration between multiple projects via the use of the .yarnrc
files. The current design it pretty simple: it simply adds the supported options from the yarnrc file at the beginning of the command line.

Some notes:

The yarnrc is currently parsed synchronously. I believe this is not an issue, since it's quite literally the very first thing we need to do when booting the application (even before the command line can be parsed), so there's really nothing we could run concurrently anyway.

The yarnrc is read in another file (https://github.com/yarnpkg/yarn/blob/master/src/registries/yarn-registry.js). I haven't changed how it works there, since I wanted my first draft to have as few changes as possible, but it's something that should probably be fixed so that the code only use one path to manage the yarnrc files.
  • Loading branch information
arcanis authored and bestander committed Apr 6, 2017
1 parent ec5c8a1 commit a5a5915
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--cache-folder foobar/hello/world
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
1 change: 1 addition & 0 deletions __tests__/fixtures/lifecycle-scripts/yarnrc-cli/.yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--cache-folder /tmp/foobar
2 changes: 2 additions & 0 deletions __tests__/fixtures/lifecycle-scripts/yarnrc-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
16 changes: 16 additions & 0 deletions __tests__/lifecycle-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ async function execCommand(cmd: string, packageName: string, env = process.env):
});
}

test('should add the yarnrc values to the command line', async () => {
const stdout = await execCommand('cache dir', 'yarnrc-cli');
expect(stdout.replace(/\\/g, '/')).toMatch(/^\/tmp\/foobar\/v[0-9]+\n$/);
});

test('should allow overriding the yarnrc values from the command line', async () => {
const stdout = await execCommand('cache dir --cache-folder /tmp/toto', 'yarnrc-cli');
expect(stdout.replace(/\\/g, '/')).toMatch(/^\/tmp\/toto\/v[0-9]+\n$/);
});

// Test disabled for now, cf rc.js
test.skip('should resolve the yarnrc values relative to where the file lives', async () => {
const stdout = await execCommand('cache dir', 'yarnrc-cli-relative');
expect(stdout.replace(/\\/g, '/')).toMatch(/^(\/[^\/]+)+\/foobar\/hello\/world\/v[0-9]+\n$/);
});

test('should expose `npm_config_argv` environment variable to lifecycle scripts for back compatibility with npm (#684)',
async () => {
const env = Object.assign({}, process.env);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"node-gyp": "^3.2.1",
"object-path": "^0.11.2",
"proper-lockfile": "^2.0.0",
"rc": "^1.2.1",
"read": "^1.0.7",
"request": "^2.81.0",
"request-capture-har": "^1.2.2",
Expand Down
6 changes: 3 additions & 3 deletions src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as network from '../util/network.js';
import {MessageError} from '../errors.js';
import aliases from './aliases.js';
import Config from '../config.js';
import {getRcArgs} from '../rc.js';
import {camelCase} from '../util/misc.js';

const chalk = require('chalk');
Expand Down Expand Up @@ -166,8 +167,7 @@ if (args.indexOf('--help') >= 0 || args.indexOf('-h') >= 0) {
process.exit(1);
}

// parse flags
args.unshift(commandName);
args = [commandName].concat(getRcArgs(commandName), args);

if (ARGS_THAT_SHARE_NAMES_WITH_OPTIONS.indexOf(commandName) >= 0 && args[0] === commandName) {
args.shift();
Expand Down Expand Up @@ -369,7 +369,7 @@ config.init({
binLinks: commander.binLinks,
modulesFolder: commander.modulesFolder,
globalFolder: commander.globalFolder,
cacheRootFolder: commander.cacheFolder,
cacheFolder: commander.cacheFolder,
preferOffline: commander.preferOffline,
captureHar: commander.har,
ignorePlatform: commander.ignorePlatform,
Expand Down
2 changes: 1 addition & 1 deletion src/lockfile/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export function* tokenise(input: string): Iterator<Token> {
} else if (input[0] === ',') {
yield buildToken(TOKEN_TYPES.comma);
chop++;
} else if (/^[a-zA-Z]/g.test(input)) {
} else if (/^[a-zA-Z\/-]/g.test(input)) {
let name = "";
for (let i = 0; i < input.length; i++) {
const char = input[i];
Expand Down
87 changes: 87 additions & 0 deletions src/rc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* @flow */

import parse from './lockfile/parse.js';

const rc = require('rc');

// Keys that will get resolved relative to the path of the rc file they belong to
const PATH_KEYS = [
'cache-folder',
'global-folder',
'modules-folder',
];

let rcConfCache;
let rcArgsCache;

const buildRcConf = () => rc('yarn', {}, [], (fileText) => {
const values = parse(fileText, 'yarnrc');
const keys = Object.keys(values);

// Unfortunately, the "rc" module we use doesn't tell us the file path :(
// cf https://github.com/dominictarr/rc/issues/61

for (const key of keys) {
for (const pathKey of PATH_KEYS) {
if (key.replace(/^(--)?([^.]+\.)+/, '') === pathKey) {
// values[key] = resolve(dirname(filePath), values[key]);
if (!values[key].startsWith('/')) {
delete values[keys];
}
}
}
}

return values;
});

export function getRcConf(): { [string]: Array<string> } {
if (!rcConfCache) {
rcConfCache = buildRcConf();
}

return rcConfCache;
}

const buildRcArgs = () => Object.keys(getRcConf()).reduce((argLists, key) => {
const miniparse = key.match(/^--(?:([^.]+)\.)?(.*)$/);

if (!miniparse) {
return argLists;
}

const namespace = miniparse[1] || '*';
const arg = miniparse[2];
const value = getRcConf()[key];

if (!argLists[namespace]) {
argLists[namespace] = [];
}

if (typeof value === 'string') {
argLists[namespace] = argLists[namespace].concat([`--${arg}`, value]);
} else {
argLists[namespace] = argLists[namespace].concat([`--${arg}`]);
}

return argLists;
}, {});

export function getRcArgs(command: string): Array<string> {
if (!rcArgsCache) {
rcArgsCache = buildRcArgs();
}

let result = rcArgsCache['*'] || [];

if (command !== '*') {
result = result.concat(rcArgsCache[command] || []);
}

return result;
}

export function clearRcCache() {
rcConfCache = null;
rcArgsCache = null;
}
9 changes: 9 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3719,6 +3719,15 @@ randombytes@^2.0.0, randombytes@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec"

rc@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
dependencies:
deep-extend "~0.4.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"

rc@~1.1.6:
version "1.1.7"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea"
Expand Down

0 comments on commit a5a5915

Please sign in to comment.