diff --git a/bin/run b/bin/run index b77a780b..0ad89bc1 100755 --- a/bin/run +++ b/bin/run @@ -1,8 +1,5 @@ #!/usr/bin/env node -/* tslint:disable:no-var-requires only-arrow-functions */ -/* eslint-disable global-require, prefer-arrow-callback */ - // Since the CLI is a single process, we can have a larger amount of max listeners since // the process gets shut down. Don't set it to 0 (no limit) since we should still be aware // of rouge event listeners @@ -14,6 +11,9 @@ process.setMaxListeners = () => {}; // Check node version before requiring additional packages require('../dist/versions').checkNodeVersion(); +// Pre-process/prune flags before creating or running the actual CLI +require('../dist/flags').preprocessCliFlags(process); + const cli = require('../dist/cli'); const pjson = require('../package.json'); diff --git a/src/flags.ts b/src/flags.ts new file mode 100644 index 00000000..79fe64ad --- /dev/null +++ b/src/flags.ts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +// ------------------------------------------------------------------------------- +// No requires or imports since this is loaded early in the cli lifecycle and we +// want to minimize the number of packages that load before enabling require +// instrumentation. +// ------------------------------------------------------------------------------- + +export interface ProcessLike { + argv: string[]; + env: { [key: string]: string | undefined }; +} + +export function preprocessCliFlags(process: ProcessLike): void { + process.argv.map((arg) => { + if (arg === '--dev-debug') { + let debug = '*'; + const filterIndex = process.argv.indexOf('--debug-filter'); + if (filterIndex > 0) { + debug = process.argv[filterIndex + 1]; + + process.argv.splice(filterIndex, 2); + } + // convert --dev-debug into a set of environment variables + process.env.DEBUG = debug; + process.env.SF_DEBUG = '1'; + process.env.SF_ENV = 'development'; + process.env.SFDX_DEBUG = '1'; + process.env.SFDX_ENV = 'development'; + + // need to calculate indexOf --dev-debug here because it might've changed based on --debug-filter + process.argv.splice(process.argv.indexOf('--dev-debug'), 1); + } + }); +} + +// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access +module.exports.preprocessCliFlags = preprocessCliFlags; diff --git a/src/versions.ts b/src/versions.ts index 4b2c7e1f..4edb8878 100644 --- a/src/versions.ts +++ b/src/versions.ts @@ -1,4 +1,3 @@ -/* eslint-disable */ /* * Copyright (c) 2022, salesforce.com, inc. * All rights reserved. @@ -8,7 +7,9 @@ import * as semver from 'semver'; // eslint-disable-next-line @typescript-eslint/no-var-requires -const pjson = require('../package.json'); +const pjson = require('../package.json') as { engines: { node: string } }; + +type Tag = string | semver.SemVer | null | undefined; /** * Determines whether or not a tag string is a semantic version. @@ -16,13 +17,14 @@ const pjson = require('../package.json'); * @param {*} tag The possible version string * @returns {boolean} True, if the string is recognized as a semantic version */ -export function isVersion(tag) { +export function isVersion(tag: Tag): boolean { if (!tag) { return false; } return !!semver.valid(tag) || false; } +// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access module.exports.isVersion = isVersion; /** @@ -32,7 +34,7 @@ export function checkNodeVersion( preferThrow = false, currentVersion = process.versions.node, requiredVersion = pjson.engines.node.slice(2) -) { +): void { if (semver.compare(currentVersion, requiredVersion) < 0) { const message = `Unsupported Node.js version ${currentVersion}, version ${requiredVersion} or later is required.`; if (!preferThrow) { @@ -45,4 +47,5 @@ export function checkNodeVersion( } } +// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access module.exports.checkNodeVersion = checkNodeVersion; diff --git a/test/cli.test.ts b/test/cli.test.ts index 0a479a52..461a67ab 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -5,8 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ // the below, there's lots of un-awaited promises for testing -/* eslint-disable no-unused-expressions*/ -/* eslint-disable @typescript-eslint/require-await*/ + import { Config, Interfaces } from '@oclif/core'; import { LoadOptions } from '@oclif/core/lib/interfaces/config'; import { stubInterface } from '@salesforce/ts-sinon'; @@ -66,7 +65,6 @@ describe('cli', () => { }); it('should initialize a Doctor instance when the doctor command is run', async () => { - debugger; sandbox.stub(Config.prototype, 'load').callsFake(() => Promise.resolve()); let loadOptions: LoadOptions; const exec = async (argv?: string[], opts?: LoadOptions): Promise => { diff --git a/test/env.test.ts b/test/env.test.ts index e1f30c90..54c446b6 100644 --- a/test/env.test.ts +++ b/test/env.test.ts @@ -5,8 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ // the below, there's lots of un-awaited promises for testing -/* eslint-disable no-unused-expressions*/ -/* eslint-disable @typescript-eslint/require-await*/ + import { expect } from 'chai'; import { EnvironmentVariable } from '@salesforce/core'; import { Env } from '../src/util/env'; diff --git a/test/flags.test.ts b/test/flags.test.ts new file mode 100644 index 00000000..d9f61c12 --- /dev/null +++ b/test/flags.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { expect } from 'chai'; +import { preprocessCliFlags, ProcessLike } from '../src/flags'; + +describe('CLI flags', () => { + it('should pass through args it does not recognize', () => { + const process: ProcessLike = { + argv: ['force:some:command', '--dev-debug', '--foo', '-f', 'bar'], + env: {}, + }; + preprocessCliFlags(process); + expect(process.argv).to.deep.equal(['force:some:command', '--foo', '-f', 'bar']); + }); + + it('should recognize --dev-debug', () => { + const process: ProcessLike = { + argv: ['--dev-debug'], + env: {}, + }; + preprocessCliFlags(process); + expect(process.argv).not.to.include('--dev-debug'); + expect(process.env.DEBUG).to.equal('*'); + expect(process.env.SF_DEBUG).to.equal('1'); + expect(process.env.SF_ENV).to.equal('development'); + expect(process.env.SFDX_DEBUG).to.equal('1'); + expect(process.env.SFDX_ENV).to.equal('development'); + }); + + it('should recognize --dev-debug in any order', () => { + const process: ProcessLike = { + argv: ['-a', 'alias', 'var=arg', '--dev-debug'], + env: {}, + }; + preprocessCliFlags(process); + expect(process.argv).not.to.include('--dev-debug'); + expect(process.env.DEBUG).to.equal('*'); + expect(process.env.SF_DEBUG).to.equal('1'); + expect(process.env.SF_ENV).to.equal('development'); + expect(process.env.SFDX_DEBUG).to.equal('1'); + expect(process.env.SFDX_ENV).to.equal('development'); + expect(process.argv.length).to.equal(3); + }); + + it('should recognize --dev-debug with a DEBUG filter', () => { + const process: ProcessLike = { + argv: ['--dev-debug', '--debug-filter', 'sf:config'], + env: {}, + }; + preprocessCliFlags(process); + expect(process.argv).to.deep.equal([]); + expect(process.argv).not.to.include('sf:Config'); + expect(process.env.DEBUG).to.equal('sf:config'); + expect(process.env.SF_DEBUG).to.equal('1'); + expect(process.env.SF_ENV).to.equal('development'); + expect(process.env.SFDX_DEBUG).to.equal('1'); + expect(process.env.SFDX_ENV).to.equal('development'); + }); + + it('should recognize --dev-debug with a DEBUG filter in any order', () => { + const process: ProcessLike = { + argv: ['--setdefault', '--debug-filter', 'sf:Config', '--dev-debug'], + env: {}, + }; + preprocessCliFlags(process); + expect(process.argv).to.deep.equal(['--setdefault']); + expect(process.argv).not.to.include('sf:Config'); + expect(process.argv).to.include('--setdefault'); + expect(process.env.DEBUG).to.equal('sf:Config'); + expect(process.env.SF_DEBUG).to.equal('1'); + expect(process.env.SF_ENV).to.equal('development'); + expect(process.env.SFDX_DEBUG).to.equal('1'); + expect(process.env.SFDX_ENV).to.equal('development'); + }); + + it('should only set DEBUG if --dev-debug is present', () => { + const process: ProcessLike = { + argv: ['--setdefault', '--debug-filter', 'sf:Config'], + env: {}, + }; + preprocessCliFlags(process); + expect(process.argv).to.deep.equal(['--setdefault', '--debug-filter', 'sf:Config']); + expect(process.env.DEBUG).to.undefined; + expect(process.env.SF_DEBUG).to.be.undefined; + expect(process.env.SF_ENV).to.undefined; + expect(process.env.SFDX_DEBUG).to.undefined; + expect(process.env.SFDX_ENV).to.undefined; + }); +});