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

refactor(ts): initial conversion to TypeScript #285

Merged
merged 13 commits into from
Jul 18, 2020

Conversation

QmarkC
Copy link
Contributor

@QmarkC QmarkC commented Jun 11, 2020

Second step for #265, "Switch to typescript".
This PR moves and converts most of the root index.js to lib/index.ts.

@QmarkC QmarkC requested review from bcoe and mleguen June 11, 2020 15:52
@bcoe
Copy link
Member

bcoe commented Jun 11, 2020

@QmarkC it seems a little weird that we saw a drop in coverage due to conversion, I'd hope we'd still be exercising the same API surface.

I'll take a closer look towards the end of my work day 👍

@QmarkC
Copy link
Contributor Author

QmarkC commented Jun 11, 2020

@QmarkC it seems a little weird that we saw a drop in coverage due to conversion, I'd hope we'd still be exercising the same API surface.

I'll take a closer look towards the end of my work day 👍

Most of the new uncovered branches are due to use of optional chaining. I forgot to run coverage before submitting the PR. I'm used to the coverage check being part of test with my other projects. I've already fixed some of them, either by removing unnecessary branches or adding tests. The original commit on this PR was 94.62 % Branch and currently its up to 96.27 % Branch. The threshold is 97% and the previous was 97.79%, so still got a little way to go (~ 5 branches at least) but that's about all I have time for today. The coverage is at 100% for all the other thresholds (lines, functions, and statements).

I also noticed that I was seeing slightly different coverage percentages when running npm run coverage vs what the coverage action is reporting. This ended up being due to difference in node version. The action uses node v13.14.0 and I was on v12.16.1. After I changed nvm to use v13.14.0 the numbers matched.

npm run coverage

-------------------------|---------|----------|---------|---------|---------------------------------
File                     | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------------|---------|----------|---------|---------|---------------------------------
All files                |     100 |    96.28 |     100 |     100 |
 yargs-parser            |     100 |      100 |     100 |     100 |
  index.js               |     100 |      100 |     100 |     100 |
 yargs-parser/lib        |     100 |    96.24 |     100 |     100 |
  index.ts               |     100 |    96.07 |     100 |     100 | ...,723,750-751,764-769,850,987
  tokenize-arg-string.ts |     100 |      100 |     100 |     100 |
-------------------------|---------|----------|---------|---------|---------------------------------
ERROR: Coverage for branches (96.28%) does not meet global threshold (97%)

coverage action

-------------------------|---------|----------|---------|---------|---------------------------------
File                     | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s               
-------------------------|---------|----------|---------|---------|---------------------------------
All files                |     100 |    96.27 |     100 |     100 |                                 
 yargs-parser            |     100 |      100 |     100 |     100 |                                 
  index.js               |     100 |      100 |     100 |     100 |                                 
 yargs-parser/lib        |     100 |    96.23 |     100 |     100 |                                 
  index.ts               |     100 |    96.05 |     100 |     100 | ...,723,750-751,764-769,850,987 
  tokenize-arg-string.ts |     100 |      100 |     100 |     100 |                                 
-------------------------|---------|----------|---------|---------|---------------------------------
ERROR: Coverage for branches (96.27%) does not meet global threshold (97%)

@QmarkC
Copy link
Contributor Author

QmarkC commented Jun 12, 2020

@bcoe branch coverage is now 98.93%. The remaining uncovered branches were previously uncovered as well.

-------------------------|---------|----------|---------|---------|---------------------
File                     | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s   
-------------------------|---------|----------|---------|---------|---------------------
All files                |     100 |    98.93 |     100 |     100 |                     
 yargs-parser            |     100 |      100 |     100 |     100 |                     
  index.js               |     100 |      100 |     100 |     100 |                     
 yargs-parser/lib        |     100 |    98.92 |     100 |     100 |                     
  index.ts               |     100 |    98.86 |     100 |     100 | 186,758-759,777,995 
  tokenize-arg-string.ts |     100 |      100 |     100 |     100 |                     
-------------------------|---------|----------|---------|---------|---------------------


export interface Options {
/** An object representing the set of aliases for a key: `{ alias: { foo: ['f']} }`. */
alias?: Dictionary<string | string[]>;
Copy link
Member

Choose a reason for hiding this comment

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

Options properties should not be all optional, for a better reuse in yargs.

You should use instead Partial<Options> when you need to use all optional options.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mleguen I'll go ahead and make this change but I'm not sure I agree with it. Perhaps you could expand on how this makes it better for reuse in yargs?

While both methods are practically the same, in my option it is a better practice to explicitly set the properties as optional for this use case. None of the options are required and they all can indeed be undefined. I think using Partial is fine when reusing an existing type and/or there are clear default values, such as with Configuration, but Options doesn't really have clear defaults for all its properties. A quick test with using Partial on the options parse argument but not with the scoped opts variable (like configuration) and setting the option properties to their fallback values (empty objects/arrays/etc...) broke a lot of tests. So we would have to use partial again there or do quite a bit of refactoring in elsewhere. From the perspective of using yargs-parser as a dependency I think the optional properties is clearer on how to interact with the module as well. Also if later an option is required, then all of the Partial<Options> uses will have to be refactored to use explicit optional properties for Options, so the explicit optional properties provides better long term support.

Copy link
Member

Choose a reason for hiding this comment

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

Perhaps you could expand on how this makes it better for reuse in yargs

I find it always easier to add undefined to a type (using ? or Partial) than to remove it.

Yargs builds an internal options structure (including yargs-parser options, but not only), none of it being optional as it deals with defaults by itself. Reusing your Options type would have helped not writing it twice.

But if it too much of a hassle to do so, no problem for me.

@mleguen
Copy link
Member

mleguen commented Jun 14, 2020

@QmarkC Sorry, I don't have a lot of bandwidth at the moment, but I will try to make a decent review of this ASAP.

@bcoe
Copy link
Member

bcoe commented Jul 1, 2020

@QmarkC likewise, I have been low bandwidth this month (I'm hoping to do some coding on the long weekend that's coming up in the USA).

This work is really exciting, I was looking at deno this evening. Along with improving yargs, I think once we're done this work on yargs/yargs-parser, it would be really easy to also provide a library for the deno community which is starting to emerge.

Copy link
Member

@bcoe bcoe left a comment

Choose a reason for hiding this comment

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

@QmarkC this is looking like a great start, I've left a few initial notes. One thing that jumps out at me, is there any approach we could take using generics that would make the return value of the parser more clever. Here's my example code:

import yargsParser from './lib/index.ts'

const argv = yargsParser(Deno.args, {
  string: ['foo']
})

function testStringType (input: string) {
  console.info(typeof input, input)
}

testStringType(argv.foo)

argv.foo ends up having an any type, it would be cool if we were able to:

  1. give the appropriate string type hints, if we've set the string key for the parse.
  2. identified that the key might be undefined, unless demand, or a default value is set.

@mleguen any thoughts on this?

const path = require('path')
const { tokenizeArgString } = require('./build/lib/tokenize-arg-string')
const util = require('util')
const { yargsParser } = require('./build/lib')
Copy link
Member

Choose a reason for hiding this comment

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

If we're converting all of index.js to index.ts, my preference would be that we just call this a breaking change and delete index.js.

package.json would then just be updated, such that its main entry point is ./build/index.js.

Comment on lines +27 to +28
import camelCase = require('camelcase')
import decamelize = require('decamelize')
Copy link
Member

Choose a reason for hiding this comment

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

One fringe benefit we get for yargs-parser out of the conversion to TypeScript, is that we can publish a version for deno.land.

Not for this PR, but I think we should vendor camelCase and decamelize. They're both fairly small deps, with 0 dependencies.

export interface Configuration {
/** Should variables prefixed with --no be treated as negations? Default is `true` */
'boolean-negation': boolean;
/** Should hyphenated arguments be expanded into camel-case aliases? Default is `true` */
Copy link
Member

Choose a reason for hiding this comment

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

these configuration keys should all be optional I believe, when testing I got:

TS2580 [ERROR]: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i @types/node`.
  process = process || {env: []}

Until I updated them to be optional.

return parse(args.slice(), opts)
}

export { yargsParser }
Copy link
Member

@bcoe bcoe Jul 2, 2020

Choose a reason for hiding this comment

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

let's make yargsParser the default export (this goes along with dropping the index.js).

@bcoe
Copy link
Member

bcoe commented Jul 16, 2020

@QmarkC wanted to bump this up, would love to get us over the finish line on some of this TypeScript work 👏

Copy link
Member

@bcoe bcoe left a comment

Choose a reason for hiding this comment

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

I'm going to land this as a starting point, in an effort to keep the TypeScript work moving along.

@bcoe bcoe changed the title refactor(ts): move and tsify most of yargs-parser to lib/index.ts refactor(ts): initial conversion to TypeScript Jul 18, 2020
@QmarkC
Copy link
Contributor Author

QmarkC commented Jul 20, 2020

I'm going to land this as a starting point, in an effort to keep the TypeScript work moving along.

Sorry for the delay. I had a death in the family. I'll catch up on the out standing items you tagged me in within the next few days.

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.

3 participants