Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: accept csv for array values #8933

Merged
merged 13 commits into from
May 22, 2019
Merged

cli: accept csv for array values #8933

merged 13 commits into from
May 22, 2019

Conversation

connorjclark
Copy link
Collaborator

@connorjclark connorjclark commented May 11, 2019

node lighthouse-cli https://www.example.com/ --only-categories=performance,seo --only-categories=pwa

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! no better motivation than a dev scorned 😆

lighthouse-cli/cli-flags.js Show resolved Hide resolved
@@ -152,6 +161,17 @@ function getFlags(manualArgv) {
.default('enable-error-reporting', undefined) // Undefined so prompted by default
.default('channel', 'cli')
.check(/** @param {LH.CliFlags} argv */ (argv) => {
// ".middleware" does not exist in this version of yargs, so do some preprocessing here.
for (const _key of Object.keys(argv)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love that this is here and I'm not sure we can actually do this across the board.

(for example --chrome-flags="--window-size 800,600")

WDYT about just doing this to our specific .array properties minus blockedUrlPatterns?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok. sounds like either way we need a hard coded list (either a blacklist of a whitelist). Whitelist is easier to reason about so let's do that :)

@@ -28,4 +28,14 @@ describe('CLI bin', function() {
assert.ok(optionsWithDescriptions.includes(opt), `cli option '${opt}' has no description`);
});
});

it('array values support csv', () => {
const flags = getFlags(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we figure out other feedback would love the --window-size and blockedUrlPattern counter examples here too :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blockedUrlPattern just b/c comma could be in a pattern? what if we could escape it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm lets not do that :)

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lint is unhappy about the tests, but other than .check I think this LGTM! :)

@@ -152,6 +161,22 @@ function getFlags(manualArgv) {
.default('enable-error-reporting', undefined) // Undefined so prompted by default
.default('channel', 'cli')
.check(/** @param {LH.CliFlags} argv */ (argv) => {
// ".middleware" does not exist in this version of yargs, so do some preprocessing here.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still not 100% sure that I love mutating the arguments in a .check method

WDYT of just saving the .argv to a variable and mutating that before we return in getFlags?

I think that might help make it more explicit that we are doing some non-standard yargs mutations

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Not sure if we want to add an example to show off our new muscles :)

@@ -171,6 +181,24 @@ function getFlags(manualArgv) {
'For more information on Lighthouse, see https://developers.google.com/web/tools/lighthouse/.')
.wrap(yargs.terminalWidth())
.argv;

// ".middleware" does not exist in this version of yargs, so do some post-processing here.
/** @type {(keyof LH.CliFlags)[]} */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're settling on Array<keyof LH.CliFlags> for complex types that aren't just string[] or w/e, though if you ask @brendankenny we're always settling on Array<*> ;)

Copy link
Member

@brendankenny brendankenny May 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though if you ask @brendankenny we're always settling on Array<*> ;)

well, yes, because it's an array, not a string, so why would you put that information last? :P They could have gone [string], but they used that up on tuples

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typing is hard for cli-flags.js...

return y.help('help')
function getFlags(...manualArgv) {
const y = manualArgv.length ? yargs(manualArgv) : yargs;
/** @type {LH.CliFlags} */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't buy us much except having to coerce with /** @type {string[]} */ below

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would like to keep /** @type {string[]} */ otherwise more errors come up. I added a comment saying why we can assume it to be a string[].

LH.CliFlags one could be removed if you want.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would like to keep /** @type {string[]} */ otherwise more errors come up.

but it's wrong because all of this is wrong :)

The ts-ignore was suppressing a different error than it said (I assume/hope it changed at some point), but the comment was still correct ('yargs() is incorrectly typed as not returning itself'): all the calls on yargs after help() are of type any and return any. In addition:

  • the type of LH.CliFlags[keyof LH.CliFlags] is definitely not a string array, really it should be LH.CliFlags['onlyAudits'|'onlyCategories'|'output'|'plugins'|'skipAudits']
  • if we pretend the type is string[] because yargs is returning that for the subset of keys we care about, assigning back as string[] is still not correct even for that subset because e.g. output is LH.OutputMode[], not string[] (and that's ignoring trying to reassign back to LH.CliFlags[keyof LH.CliFlags]). This will be an error in tsc 3.5+.

I think the most honest thing is to just leave as any internal to getFlags with a note about why and leave it up to tests to verify that getFlags really is returning LH.CliFlags.

lighthouse-cli/cli-flags.js Show resolved Hide resolved
const y = manualArgv ? yargs(manualArgv) : yargs;
return y.help('help')
function getFlags(...manualArgv) {
const y = manualArgv.length ? yargs(manualArgv) : yargs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this ends up somewhat confusing from the caller's perspective since it's no longer explicitly optional (even though we usually call it with no args) and when we do pass an argument it's still a single string (it's annoying that the yargs types require this because yargs definitely supports parsing just a string).

What about keeping it string= and doing const y = manualArgv ? yargs([manualArgv]) : yargs; instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused on how it's confusing to the caller?

if reverting the change, would you want the code using multiple args (admittedly it's just test code) to look like this?

getFlags([
	'http://www.example.com',
	'--only-categories=performance,seo',
	'--skipAudits=unused-javascript,redirects',
	'--skipAudits=bootup-time',
].join(' '));

or this?

getFlags('http://www.example.com --only-categories=performance,seo --skipAudits=unused-javascript,redirects --skipAudits=bootup-time');

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if reverting the change, would you want the code using multiple args (admittedly it's just test code) to look like this?

yes from me. Maybe it's just that I haven't been completely indoctrinated to rest parameters yet, but to me an optional manualArgv?: string parameter is a lot clearer than ...manaulArgv: string[] at informing that the argument is optional. It's not a huge deal since it's not a commonly used interface, but it's only test code that uses this, so it makes sense to me to make the real use case clear in the signature.

.version(() => pkg.version)
.showHelpOnFail(false, 'Specify --help for available options')

.usage('lighthouse <url> <options>')
.example(
'lighthouse <url> --view', 'Opens the HTML report in a browser after the run completes')
.example(
'lighthouse <url> --only-categories=performance,pwa',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kick somewhere below? I'd call --only-categories less important than --config-path, --chrome-flags, etc

lighthouse-cli/cli-flags.js Show resolved Hide resolved
const y = manualArgv ? yargs(manualArgv) : yargs;
return y.help('help')
function getFlags(...manualArgv) {
const y = manualArgv.length ? yargs(manualArgv) : yargs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha, actually, that test failure may be because our version of yargs doesn't take an array at all?

Copy link
Collaborator Author

@connorjclark connorjclark May 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It passes locally.

(EDIT: misread the build log. one sec)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok the issue is that if you pass in an array, you cannot have each item have multiple args. test just needs to change

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also I guess they expect some preprocessing, b/c = is required too. 6f58b90

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also I guess they expect some preprocessing, b/c = is required too.

You can only omit the = when passing in as a single string? This seems like the real downside...we should be able to write tests using both styles since the real deal accepts both

@connorjclark
Copy link
Collaborator Author

@brendankenny updated

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, just some last comment feedback

lighthouse-cli/cli-flags.js Show resolved Hide resolved
const result = [];
return result.concat(...arr);
}

/**
* @param {string=} manualArgv
* @return {LH.CliFlags}
*/
function getFlags(manualArgv) {
// @ts-ignore yargs() is incorrectly typed as not returning itself
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe update this one as well? // @ts-ignore yargs() is incorrectly typed as not accepting a single string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly why are we even using @types/yargs :)

// since assigning back to argv as string[] would be unsound for enums,
// for example: output is LH.OutputMode[].
const input = argv[key];
// Existence check + convinces TS that this is an array.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Existence check + "...something else? Or no '+' needed

lighthouse-cli/cli-flags.js Outdated Show resolved Hide resolved
Co-Authored-By: Brendan Kenny <bckenny@gmail.com>
@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ Googlers: Go here for more info.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants