diff --git a/__tests__/fixtures/lifecycle-scripts/npm_config_argv_env_vars/log-command.js b/__tests__/fixtures/lifecycle-scripts/npm_config_argv_env_vars/log-command.js new file mode 100644 index 0000000000..725005575a --- /dev/null +++ b/__tests__/fixtures/lifecycle-scripts/npm_config_argv_env_vars/log-command.js @@ -0,0 +1,7 @@ +try { + const argv = JSON.parse(process.env['npm_config_argv']); + + console.log(`##${argv.cooked[0]}##`); +} catch (err) { + console.log(`##${err.toString()}##`); +} diff --git a/__tests__/fixtures/lifecycle-scripts/npm_config_argv_env_vars/package.json b/__tests__/fixtures/lifecycle-scripts/npm_config_argv_env_vars/package.json new file mode 100644 index 0000000000..e01fd2a6f8 --- /dev/null +++ b/__tests__/fixtures/lifecycle-scripts/npm_config_argv_env_vars/package.json @@ -0,0 +1,10 @@ +{ + "name": "npm_config_argv_env_vars", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "scripts": { + "prepublish" : "node log-command.js", + "pretest" : "node log-command.js" + } +} diff --git a/__tests__/lifecycle-scripts.js b/__tests__/lifecycle-scripts.js new file mode 100644 index 0000000000..4c396a3733 --- /dev/null +++ b/__tests__/lifecycle-scripts.js @@ -0,0 +1,39 @@ +/* @flow */ + +import makeTemp from './_temp'; +import * as fs from '../src/util/fs.js'; + +const path = require('path'); +const exec = require('child_process').exec; + +const fixturesLoc = path.join(__dirname, './fixtures/lifecycle-scripts'); +const yarnBin = path.join(__dirname, '../bin/yarn.js'); + +async function execCommand(cmd: string, packageName: string): Promise { + const srcPackageDir = path.join(fixturesLoc, packageName); + const packageDir = await makeTemp(packageName); + + await fs.copy(srcPackageDir, packageDir); + + return new Promise((resolve, reject) => { + exec(`node ${yarnBin} ${cmd}`, {cwd:packageDir}, (err, stdout) => { + if (err) { + reject(err); + } else { + resolve(stdout.toString()); + } + }); + }); +} + +test('should expose `npm_config_argv` environment variable to lifecycle scripts for back compatibility with npm (#684)', +async () => { + let stdout = await execCommand('install', 'npm_config_argv_env_vars'); + expect(stdout).toContain('##install##'); + + stdout = await execCommand('', 'npm_config_argv_env_vars'); + expect(stdout).toContain('##install##'); + + stdout = await execCommand('test', 'npm_config_argv_env_vars'); + expect(stdout).toContain('##test##'); +}); diff --git a/src/cli/index.js b/src/cli/index.js index 6b13a508f2..c51afb95b6 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -328,6 +328,7 @@ config.init({ ignoreEngines: commander.ignoreEngines, offline: commander.preferOffline || commander.offline, looseSemver: !commander.strictSemver, + commandName, }).then(() => { const exit = () => { process.exit(0); diff --git a/src/config.js b/src/config.js index 8eb9d75762..5d036d197c 100644 --- a/src/config.js +++ b/src/config.js @@ -33,6 +33,7 @@ export type ConfigOptions = { // Loosely compare semver for invalid cases like "0.01.0" looseSemver?: ?boolean, + commandName?: ?string }; type PackageMetadata = { @@ -115,6 +116,9 @@ export default class Config { [key: string]: ?Promise }; + // + commandName: string; + /** * Execute a promise produced by factory if it doesn't exist in our cache with * the associated key. @@ -194,6 +198,8 @@ export default class Config { this.looseSemver = opts.looseSemver == undefined ? true : opts.looseSemver; + this.commandName = opts.commandName || ''; + this.preferOffline = !!opts.preferOffline; this.modulesFolder = opts.modulesFolder; this.globalFolder = opts.globalFolder || constants.GLOBAL_MODULE_DIRECTORY; diff --git a/src/util/execute-lifecycle-script.js b/src/util/execute-lifecycle-script.js index bc1109903b..3098886dc1 100644 --- a/src/util/execute-lifecycle-script.js +++ b/src/util/execute-lifecycle-script.js @@ -25,6 +25,10 @@ async function makeEnv(stage: string, cwd: string, config: Config): { env.npm_node_execpath = env.NODE || process.execPath; env.npm_execpath = path.join(__dirname, '..', '..', 'bin', 'yarn.js'); + // Note: npm_config_argv environment variable contains output of nopt - command-line + // parser used by npm. Since we use other parser, we just roughly emulate it's output. (See: #684) + env.npm_config_argv = JSON.stringify({remain:[], cooked: [config.commandName], original: [config.commandName]}); + // add npm_package_* const manifest = await config.readManifest(cwd); const queue = [['', manifest]];