Skip to content

Conversation

@Lezek123
Copy link
Contributor

@Lezek123 Lezek123 commented May 29, 2020

This PR includes some adjustments to the Pioneer's eslint rules and fixes all the remaining eslint errors. This should allow us to add the linter check for Pioneer as part of the CI again and start paying attention to it from now on. Overall I think Pioneer's codebase is now going to be in a considerable better shape!

Some rules that I removed:
And the reasoning behind this

  • @typescript-eslint/camelcase - this one doesn't make much sense for Pioneer, because it would force us to use camelcase for all objects' property names etc., which as I experienced, can cause some issues with Structs that we use to interact with the runtime (if we use camelcase and runtime uses snakecase, the properties may not get populated correctly ie. when sending extrinsics)
  • react/prop-types - normally this rule checks if we have propTypes correctly defined in all of the react components, but it seems to give some unreliable results when working with TypeScript (ie. sometimes using a ts type for props is enough, but sometimes eslint has some trouble with infering the right types etc.). TypeSciprt would normally give us a build error if we use incorrect typing for props, so I'd say this rule isn't really necessary.
  • new-cap - this rule gives us errors in all places we use new lowercaseClassName(), but this usually regards classes imported from packages like @polkadot/types (ie. u16, u32 etc.) and not the ones we define ourselfs (which give us other type of error).
  • @typescript-eslint/interface-name-prefix - this rule I think is not very useful in general. According to it it's a bad practice to use interface names like IConfig, but sometimes I think it just makes sense, ie. we have a class like FinalizationData, which we use to register the type for the api and an additional interface/type for that class called IFinalizationData (which we use for JoyStruct for example)

Some common problems I fixed:
(> 10 errors per rule, each of those has a separate commit)

  • 3da9de8 - first I performed an autofix using eslint with --fix flag. This handles some whitespace errors, swtiches some characters like ', ", ; and , in places where those changes shouldn't really affect anything etc. (just making things consistent). Applying this fix already removed ~80% of the errors. Since I think it's very useful, I also made it available through yarn workspace pioneer lint-autofix.
  • no-mixed-operators (e4548e3) - this seems to be reasonable rule preventing the use of A && B || C without parenthesis (which may be confusing). Here I think that pretty much for all the occurences it was enought to just replace those with (A && B) || C (logically they already made sense)
  • react/no-unescaped-entities (3d08c44) - this rule prevents the use of characters like ', ", < and > inside jsx direct-child strings, ie. <div>></div>, since there is a high chance that it is a mistake. In case of Pioneer those weren't mistakes, but I think the rule is still worth preserving so I fixed those by changing strings like Backers' stake to analogous expressions, like: {'Backers\' stake'}.
  • typescript-eslint/no-use-before-define (9f3cb13) - the name of the rule is pretty self-explainatory. Although we can use functions etc. before we define them, it's better to avoid this as this is normally not the case with let, const etc. The fix here was mostly about moving the code around, so that everything is correctly defined before used.
  • eqeqeq (c6549d8) - this rule prevents us from using == and !=. Assuming our TS typing is correct, it was safe to just replace those with === and !==, so that's what I did here.
  • @typescript-eslint/no-empty-function (0b64ddf) - according to this rule using syntax like () => {} may be confusing, because it's not clear whether we want the function to just do nothing or return and empty object. Changing it to () => {/* do nothing */} is enough to signal the intention and silence those kind of errors.
  • react/display-name (cd2d440) - this I think is a very good rule, which forces use to set component display name in all places where it cannot be easily derived (ie. from class/function name), so mostly in our HOCs. This is also mentioned in the react documentation (https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging). Thanks to fixing this we will now get a cool, descriptive component name like withMembershipRequired(SomeComponent) instead of undefined in react warnings/errors, which will greatly increase the readability of those. This fix required a bit more coding, but in that case I'm pretty sure it's going to be worth it.
  • react/jsx-key (da495fb) - React requires to specify key prop when using lists/arrays of jsx elements. Not using those results also in a console warning/error, since it may negatively affect the speed of rendering the lists etc. Fixed by adding key prop where necessary.
  • no-mixed-spaces-and-tabs / no-tabs (150a28b) - those were some whitespace issues that could reduce the readibility of the code, but for some reason they couldn't be fixed automatically.

After fixing all the issues listed above, there were still 67 remaining errors. I fixed those in the last commit (2e67b25). Some I silenced using eslint-disable-next-line, like when we were using async function without await in mock transports (since they needed to return the promise in order to be consistent with the "real" transport).

Here's the list of issues fixed in that commit:

/home/leszek/projects/joystream/joystream/pioneer/packages/app-staking/src/Overview/index.tsx
  32:12  error  Bad line breaking before and after '?'  operator-linebreak
  34:12  error  Bad line breaking before and after ':'  operator-linebreak

/home/leszek/projects/joystream/joystream/pioneer/packages/apps/src/SideBar/index.tsx
  39:60  error  Using target="_blank" without rel="noopener noreferrer" is a security risk: see https://mathiasbynens.github.io/rel-noopener  react/jsx-no-target-blank

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-help/src/Help.tsx
  35:65  error  Using target="_blank" without rel="noopener noreferrer" is a security risk: see https://mathiasbynens.github.io/rel-noopener  react/jsx-no-target-blank

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/DiscoveryProvider.tsx
   4:10  error  'url.parse' was deprecated since v11.0.0. Use 'url.URL' constructor instead  node/no-deprecated-api
  29:14  error  Do not use String as a constructor                                           no-new-wrappers
  51:23  error  'bootstrapNodes' is not modified in this loop                                no-unmodified-loop-condition

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/IterableFile.ts
  4:11  error  Interface name must not be prefixed with "I"  @typescript-eslint/interface-name-prefix

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/TransportContext.tsx
  24:27  error  Async arrow function has no 'await' expression  @typescript-eslint/require-await

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/Upload.tsx
  198:47  error  Async arrow function has no 'await' expression  @typescript-eslint/require-await
  261:7   error  Unexpected var, use let or const instead        no-var
  317:7   error  Unexpected var, use let or const instead        no-var

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/channels/ChannelPreview.tsx
  29:5   error  Expected an assignment or function call and instead saw an expression  no-unused-expressions
  29:31  error  Unexpected use of comma operator                                       no-sequences

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/music/ReorderableTracks.tsx
  54:18  error  Avoid referencing unbound methods which may cause unintentional scoping of `this`  @typescript-eslint/unbound-method
  61:26  error  Avoid referencing unbound methods which may cause unintentional scoping of `this`  @typescript-eslint/unbound-method

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/transport.ts
  87:17  error  Unexpected `await` of a non-Promise (non-"Thenable") value  @typescript-eslint/await-thenable

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-media/src/video/PlayVideo.tsx
  59:32  error  Using target="_blank" without rel="noopener noreferrer" is a security risk: see https://mathiasbynens.github.io/rel-noopener  react/jsx-no-target-blank

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx
  70:33  error  'new Buffer()' was deprecated since v6.0.0. Use 'Buffer.alloc()' or 'Buffer.from()' instead  node/no-deprecated-api

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupMintCapForm.tsx
  2:21  error  Use default import syntax to import 'MintCapacityForm'  import/no-named-default

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-proposals/src/forms/errorHandling.ts
  34:10  error  Use 'as FormErrorLabelsProps<ValuesT>' instead of '<FormErrorLabelsProps<ValuesT>>'  @typescript-eslint/consistent-type-assertions

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-proposals/src/validationSchema.ts
  245:29  error  Unexpected template string expression  no-template-curly-in-string

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/jest.config.js
  1:37  error  Require statement not part of import statement  @typescript-eslint/no-var-requires
  4:29  error  Require statement not part of import statement  @typescript-eslint/no-var-requires

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/StakeRequirement.tsx
  11:18  error  Interface name must not be prefixed with "I"  @typescript-eslint/interface-name-prefix

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/classifiers.spec.ts
  17:7  error  Class 'mockBlockQueryer' must be PascalCased                @typescript-eslint/class-name-casing
  29:3  error  Async method 'blockHash' has no 'await' expression          @typescript-eslint/require-await
  33:3  error  Async method 'blockTimestamp' has no 'await' expression     @typescript-eslint/require-await
  37:3  error  Async method 'expectedBlockTime' has no 'await' expression  @typescript-eslint/require-await

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/classifiers.ts
  53:18  error  Interface name must not be prefixed with "I"  @typescript-eslint/interface-name-prefix

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/flows/apply.controller.tsx
  165:3  error  Async method 'prepareApplicationTransaction' has no 'await' expression  @typescript-eslint/require-await

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/flows/apply.tsx
   30:1   error  Do not use "// @ts-ignore" comments because they suppress compilation errors  @typescript-eslint/ban-ts-ignore
  753:11  error  Interface 'finalDataMap' must be PascalCased                                  @typescript-eslint/class-name-casing
  774:11  error  Interface 'questionDataMap' must be PascalCased                               @typescript-eslint/class-name-casing

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/tabs/MyRoles.tsx
  92:11  error  Interface 'nameAndURL' must be PascalCased  @typescript-eslint/class-name-casing

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/tabs/Opportunities.tsx
  118:7  error  Class 'messageState' must be PascalCased  @typescript-eslint/class-name-casing

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/transport.mock.ts
  353:3  error  Async method 'openingApplications' has no 'await' expression    @typescript-eslint/require-await
  425:3  error  Async method 'myCurationGroupRoles' has no 'await' expression   @typescript-eslint/require-await
  441:3  error  Async method 'applyToCuratorOpening' has no 'await' expression  @typescript-eslint/require-await

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/transport.substrate.ts
   58:11  error  Interface name must not be prefixed with "I"                                  @typescript-eslint/interface-name-prefix
  144:37  error  Promise returned in function argument where a void return was expected        @typescript-eslint/no-misused-promises
  144:37  error  Promise executor functions should not be async                                no-async-promise-executor
  150:9   error  Expected the Promise rejection reason to be an Error                          prefer-promise-reject-errors
  325:45  error  Promise returned in function argument where a void return was expected        @typescript-eslint/no-misused-promises
  325:45  error  Promise executor functions should not be async                                no-async-promise-executor
  328:9   error  Expected the Promise rejection reason to be an Error                          prefer-promise-reject-errors
  388:7   error  Do not use "// @ts-ignore" comments because they suppress compilation errors  @typescript-eslint/ban-ts-ignore
  586:32  error  Promise returned in function argument where a void return was expected        @typescript-eslint/no-misused-promises
  586:32  error  Promise executor functions should not be async                                no-async-promise-executor
  591:9   error  Expected the Promise rejection reason to be an Error                          prefer-promise-reject-errors
  596:9   error  Expected the Promise rejection reason to be an Error                          prefer-promise-reject-errors
  608:9   error  Expected the Promise rejection reason to be an Error                          prefer-promise-reject-errors

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-roles/src/transport.ts
  11:18  error  Interface name must not be prefixed with "I"  @typescript-eslint/interface-name-prefix

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/Controller.tsx
  8:3  error  Useless constructor  no-useless-constructor

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/Loadable.tsx
  12:18  error  Do not access Object.prototype method 'hasOwnProperty' from target object  no-prototype-builtins

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/MyAccountContext.tsx
  52:7  error  Unexpected lexical declaration in case block  no-case-declarations

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/Subscribable.ts
   8:18  error  Interface name must not be prefixed with "I"  @typescript-eslint/interface-name-prefix
  12:18  error  Interface name must not be prefixed with "I"  @typescript-eslint/interface-name-prefix

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/Transport.ts
  3:27  error  Promise returned in function argument where a void return was expected  @typescript-eslint/no-misused-promises
  3:27  error  Promise executor functions should not be async                          no-async-promise-executor
  5:15  error  Promise constructor parameters must be named resolve, reject            promise/param-names

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/index.ts
  104:21  error  Empty block statement  no-empty

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/memo/MemoView.tsx
  11:16  error  Require statement not part of import statement  @typescript-eslint/no-var-requires
  12:15  error  Require statement not part of import statement  @typescript-eslint/no-var-requires

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/memoize.ts
  13:17  error  Do not access Object.prototype method 'hasOwnProperty' from target object  no-prototype-builtins
  31:16  error  Do not access Object.prototype method 'hasOwnProperty' from target object  no-prototype-builtins

/home/leszek/projects/joystream/joystream/pioneer/packages/joy-utils/src/transport/proposals.ts
  54:14  error  Unexpected `await` of a non-Promise (non-"Thenable") value  @typescript-eslint/await-thenable

@Lezek123 Lezek123 marked this pull request as draft May 29, 2020 13:40
@Lezek123 Lezek123 marked this pull request as ready for review May 29, 2020 16:04
@Lezek123 Lezek123 requested review from fulminmaxi and mnaamani May 29, 2020 16:04
@Lezek123
Copy link
Contributor Author

I tried to test as much as I could, unfortunately most of the fixes are regarding joy-roles which has a lot of features which I'm not sure how to test.

@mnaamani
Copy link
Member

mnaamani commented Jun 2, 2020

Needs a merge from development to resolve conflict after #514 was merged.

In that PR though, I did break the linter by using double quotes instead of single quotes:

import Transport from "../../transport/index";

in

/Users/mokhtar/joystream/joystream/pioneer/packages/joy-utils/src/react/context/transport.tsx
  5:23  error    Strings must use singlequote     quotes

I tested storage app didn't find any issues.

In the content working group I'm observing a bug after creating an opportunity and trying to apply I get an empty loading page..

Screen Shot 2020-06-02 at 6 05 52 PM

Which goes on forever, only after adjusting the windows size do I get:

Screen Shot 2020-06-02 at 6 06 13 PM

I then had to switch to another app and come back to the working group app for application form to appear when applying..

Screen Shot 2020-06-02 at 6 10 15 PM

Will test if this behavior also happens on current development branch.

During this test I did see the following error in console which might explain what is going on.

Warning: Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. You can only call Hooks at the top level of your React function. For more information, see https://fb.me/rules-of-hooks
    in Unknown (created by Context.Consumer)
    in Route (created by App)
    in Switch (created by App)
    in main (created by App)
    in App (created by withCurationActor(App))
    in withCurationActor(App) (created by withMyRoles(withCurationActor(App)))
    in withMyRoles(withCurationActor(App)) (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by resolveLead(WithApi))
    in resolveLead(WithApi) (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by withMyMembership(WithApi))
    in withMyMembership(WithApi) (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by withMyAddress(WithApi))
    in withMyAddress(WithApi) (created by WithObservable)
    in WithObservable (created by withI18nextTranslation(WithObservable))
    in withI18nextTranslation(WithObservable) (created by Content)
    in div (created by Context.Consumer)
    in StyledComponent (created by Content__Wrapper)
    in Content__Wrapper (created by Content)
    in div (created by Content)
    in Content (created by Context.Consumer)
    in StyledComponent (created by Content)
    in Content (created by Context.Consumer)
    in withRouter(Content) (created by withI18nextTranslation(withRouter(Content)))
    in withI18nextTranslation(withRouter(Content)) (created by Apps)
    in Signer (created by Apps)
    in div (created by Apps)
    in Apps (created by Context.Consumer)
    in StyledComponent (created by Apps)
    in Apps (created by Context.Consumer)
    in ThemeProvider (created by Context.Consumer)
    in Router (created by HashRouter)
    in HashRouter (created by Context.Consumer)
    in Events (created by Context.Consumer)
    in BlockAuthors (created by Context.Consumer)
    in InnerMyMembershipProvider (created by withCurationActor(InnerMyMembershipProvider))
    in withCurationActor(InnerMyMembershipProvider) (created by withMyRoles(withCurationActor(InnerMyMembershipProvider)))
    in withMyRoles(withCurationActor(InnerMyMembershipProvider)) (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by resolveLead(WithApi))
    in resolveLead(WithApi) (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by withMyMembership(WithApi))
    in withMyMembership(WithApi) (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by WithPromise)
    in WithPromise (created by Context.Consumer)
    in WithApi (created by withMyAddress(WithApi))
    in withMyAddress(WithApi) (created by WithObservable)
    in WithObservable (created by Context.Consumer)
    in Api (created by Context.Consumer)
    in Queue
    in MyAccountProvider
    in Suspense

@Lezek123
Copy link
Contributor Author

Lezek123 commented Jun 3, 2020

Resolved the conflicts with development branch.

@mnaamani
Copy link
Member

mnaamani commented Jun 3, 2020

The issue described above is also found on development branch, so it was NOT introduced in this PR.

Copy link
Member

@mnaamani mnaamani left a comment

Choose a reason for hiding this comment

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

I can't find anything broken following some manual testing (using the apps)

@Lezek123 Lezek123 marked this pull request as draft June 3, 2020 11:40
@Lezek123
Copy link
Contributor Author

Lezek123 commented Jun 3, 2020

There is one issue I uncovered doing some further tests now. Converting to draft and will add a fix soon.

@Lezek123
Copy link
Contributor Author

Lezek123 commented Jun 3, 2020

Doing some more extensive tests in joy-roles now I noticed that I accidently used the wrong syntax for strings that were combined with jsx in the react/no-unescaped-entities fix commit (3d08c44), ie.:

`you'll need to select ${<strong>{props.roleKeyName}</strong>} in the accounts selector`

Nesting jsx like that results in a string containing [Object object], ie.:

you'll need to select [Object object] in the accounts selector

This should be fixed now.
Other than that I didn't find any issues that wouldn't also appear on the development branch.

@Lezek123 Lezek123 marked this pull request as ready for review June 3, 2020 12:29
@Lezek123 Lezek123 requested a review from mnaamani June 3, 2020 12:29
@mnaamani
Copy link
Member

mnaamani commented Jun 3, 2020

Perhaps we can merge Lezek123#6 to add the linter check to the CI checks

Also I'm thinking of merging this work into the upcoming release branch.

@Lezek123
Copy link
Contributor Author

Lezek123 commented Jun 3, 2020

I did some tests and I noticed that there are a few different approaches we could use to add linting into the CI:

  1. Add yarn workspace pioneer lint as another command right after the build (the way it's done here: Add linter check to github workflow pioneer jobs Lezek123/substrate-runtime-joystream#6) - the problem with this approach is that if the linter check fails, the CI won't even get into the build step (and vice-versa), so the output from the CI is not as clear as it could be.
  2. Create separate jobs for linting - this seems like a very good solution both in terms of execution time and "clarity" of the result, as long as we don't mind having a lot of separate jobs (this is also the approach that was used before in the Joystream/apps repository). In the end I decided to go for this one.
  3. Add linting inside the "Build" job, but as a separate step - I thought it may be a good compromise between 1. and 2., allowing us to run the "build" and "lint" steps separately inside the same job (using a new section with - name: lint), but turns out the lint check gets skipped anyway if the build fails, so it works more or less the same way solution 1. does.

For more context see: https://github.com/Lezek123/substrate-runtime-joystream/commits/break-ci-lint-check

@Lezek123 Lezek123 requested a review from mnaamani June 3, 2020 18:32
@mnaamani
Copy link
Member

mnaamani commented Jun 4, 2020

2. Create separate jobs for linting - this seems like a very good solution both in terms of execution time and "clarity" of the result, as long as we don't mind having a lot of separate jobs (this is also the approach that was used before in the `Joystream/apps` repository). **In the end I decided to go for this one.**

I'm happy with that approach except I'm not sure if there is any added value in doing the linter check on more than one platform. There is no point in removing it now, unless we need to reduce the number of concurrent jobs.

LGTM

@mnaamani mnaamani merged commit d28953a into Joystream:development Jun 4, 2020
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