GithubHelp home page GithubHelp logo

relayjs / eslint-plugin-relay Goto Github PK

View Code? Open in Web Editor NEW
97.0 20.0 42.0 520 KB

A plugin for the code linter ESLint to lint specific details about Relay.

License: MIT License

JavaScript 100.00%

eslint-plugin-relay's Introduction

eslint-plugin-relay Build Status npm version

eslint-plugin-relay is a plugin for ESLint to catch common problems in code using Relay early.

Install

npm i --save-dev eslint-plugin-relay

How To Use

  1. Add "relay" to your eslint plugins section.
  2. Add the relay rules such as "relay/graphql-syntax": "error" to your eslint rules section, see the example for all rules.

Example .eslintrc.js:

module.exports = {
  // Other eslint properties here
  rules: {
    'relay/graphql-syntax': 'error',
    'relay/compat-uses-vars': 'warn',
    'relay/graphql-naming': 'error',
    'relay/generated-flow-types': 'warn',
    'relay/must-colocate-fragment-spreads': 'warn',
    'relay/no-future-added-value': 'warn',
    'relay/unused-fields': 'warn',
    'relay/function-required-argument': 'warn',
    'relay/hook-required-argument': 'warn'
  },
  plugins: ['relay']
};

You can also enable all the recommended or strict rules at once. Add plugin:relay/recommended or plugin:relay/strict in extends:

{
  "extends": [
    "plugin:relay/recommended"
  ]
}

Suppressing rules within graphql tags

The following rules support suppression within graphql tags:

  • relay/unused-fields
  • relay/must-colocate-fragment-spreads

Supported rules can be suppressed by adding # eslint-disable-next-line relay/name-of-rule to the preceding line:

graphql`fragment foo on Page {
  # eslint-disable-next-line relay/must-colocate-fragment-spreads
  ...unused1
}`

Note that only the eslint-disable-next-line form of suppression works. eslint-disable-line doesn't currently work until graphql-js provides support for parsing Comment nodes in their AST.

Contribute

We actively welcome pull requests, learn how to contribute.

License

eslint-plugin-relay is MIT licensed.

eslint-plugin-relay's People

Contributors

aabmass avatar acconrad avatar alloy avatar alunyov avatar barillari avatar captbaritone avatar dependabot[bot] avatar flarnie avatar janicduplessis avatar josephsavona avatar jstejada avatar kassens avatar martindawson avatar mjmahone avatar mroch avatar natelindev avatar poteto avatar pranaygp avatar reckter avatar spyl94 avatar steida avatar stephen2 avatar sullenor avatar tbergquist-godaddy avatar trysound avatar tyao1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eslint-plugin-relay's Issues

Follow intersection types for the generated-flow-types lint rule

In the case somebody is using Intersection types (https://flow.org/en/docs/types/intersections/#toc-impossible-intersection-types)

We should follow the intersected type, and if it's and object, check its props. If not an object, and is instead an imported type, we can:

  • Not do anything (being conservative, since we don't know for sure if that imported type uses the right prop type)
  • Lint as if the intersected type annotation doesn't exist (being aggressive, though unlikely that someone would put their imported relay type annotation in something that needs to be intersected into the type used by the component props, but this would lead to false positives if they did)

Relay 13: some explicit types are no longer required

For example, before Relay 13, it was required to annotate useLazyLoadQuery like so:

const data = useLazyLoadQuery<NavigationHeaderBadgeQuery>(graphql` โ€ฆ `);

However, this is no longer required when used together with relay_integration=true Flow option, see: facebook/relay@3c45f95

This issue proposes to remove or change relay/generated-flow-types rule to support this new way of using Relay 13.

Setup Travis

We should setup travis especially to ensure node 4.x compatibility.

Would you consider adding lint for deprecated

Hello, i'm trying to add a linter for deprecrated fields on my project,

our schema is quite large, and i struggle to find a linter that could do it well...
i was wondering if you would consider adding one in the project?

i might try my hand at it

readInlineData: missing argument check

Hey, I faced an issue recently with readInlineData function call. I forgot to pass fragment reference (second argument) to readInlineData and had some challenges to figure out why my feature wasn't working properly.

Flow also haven't covered this case, so I thought that it would be nice to have a rule check similar to rule-hook-required-argument.

For context here is a link for the doc https://relay.dev/docs/en/graphql-in-relay#inline

unused-fields doesn't support fragments spread

Hi! I am not sure whether this is a bug report or rather a feature request. I noticed that it's possible to spread a fragment even though it's never actually being used. I believe this rule should also catch this case since it leads to overfetching.

 ...LabelsList_data
 ...LabelsListSelect_data
 labels {
-	...Label_label   # not needed but not reported
 	id
 	name
 }

error when user forgot `on User` on fragment

I've got the error below when I forgot to have on User on my fragment for instance:

user: graphql`
    fragment MyComponent_user {
}
TypeError: Cannot read property 'kind' of null
    at walkAST (/Users/sibelius/Dev/app-test/node_modules/eslint-plugin-relay/src/rule-unused-fields.js:21:14)
    at getGraphQLFieldNames (/Users/sibelius/Dev/app-test/node_modules/eslint-plugin-relay/src/rule-unused-fields.js:51:3)
    at Program:exit.templateLiterals.forEach.templateLiteral (/Users/sibelius/Dev/app-test/node_modules/eslint-plugin-relay/src/rule-unused-fields.js:132:31)
    at Array.forEach (<anonymous>)
    at Program:exit (/Users/sibelius/Dev/app-test/node_modules/eslint-plugin-relay/src/rule-unused-fields.js:129:24)

Should we handle this case?

graphql-naming rule doesn't seem to work on fragments in useFragment hook

I'm not entirely sure because the naming linting does apply to query names, but not fragment names (on relay v14.0.0)

In the code for the graphql-naming rule I see this:

const CREATE_CONTAINER_FUNCTIONS = new Set([
  'createFragmentContainer',
  'createPaginationContainer',
  'createRefetchContainer'
]);

It seems like it's just checking these container functions which aren't in the latest relay which uses hooks like useFragment.

No line numbers for unused-fields?

Thanks for creating this ๐Ÿ™๐Ÿป
I am trying to add the rules to my CRA v4 project. And while the rules do work I get no line numbers for unused-fields.

Not sure if this is a problem on my end or a limitation of the rule?

Do you get line numbers along with the warnings from unused-fields?

Detect unused fields

If a component queries for some fields, we should try to detect if some of those fields are not used in the component and might indicate overfetching.

What's the plan for the future of this plugin?

There have been no releases despite there having been active contributions.

We at @contra heavily rely on Relay and would want to have a functioning ESLint plugin.

Do you need help maintaining this package?

If you are not planning on maintaining this package, would you be open to handing over the namespace?

We are leaning towards publishing eslint-plugin-relay fork, but I wanted to discuss with the existing maintainers before taking any initiative here.

TypeScript support?

I'm working on an overdue migration from TSLint to ESLint due to the former's deprecation in favor of the latter. I had been using a plugin called tslint-plugin-relay, which ported rules from this project over to TypeScript. Is there any interest in supporting TypeScript rules to parallel the Flow rules? If so, I'd be happy to work on upstreaming work from tslint-plugin-relay. If not, I can work on separate plugin instead.

must-colocate-fragment-spreads fails on sibling index.js imports

I've described it in some detail in the PR that fixes this issue here #104, but I'm opening an issue to track it. If that's not the right way to do this, then feel free to close this issue.

The problem code:

        import MyComponent from './';

        graphql`fragment foo on Page {
          ...MyComponent_foo
        }`

Currently throws the error:

This spreads the fragment MyComponent_foo but this module does not use it directly. If a different module needs this information, that module should directly define a fragment querying for that data, colocated next to where the data is used.

Which is highly confusing, as it is imported.

I believe this is an edge-case surfaced by our pattern of importing relative components in the same directory, so I've tried to tweak the module parser to handle this by checking the name of the parent directory in my PR.

Another option would be to operate on the variable name but that seems far worse and too much like the strict horrors of Relay compat.

RFC: add an `ignore` option to the `must-colocate-fragment-spreads` rule

Why

We encounter the following pattern:

  • There is a parent component, UserList, that defines a callback onSelectUser, which accepts an @inline fragment named UserList_onSelectUser_user.
  • This callback is passed to a UserDetail component as props.onSelectUser, to which a user is passed where ...UserList_onSelectUser_user is spread.
  • This eslint rule is not happy, and there is no sane way to work around this restriction, as far as I can tell. (One would have to import UserList from the child component, which is just silly!)
  • We do not want to allow users to suppress this rule, as in 99% of cases, this is a rule we don't want to let users suppress! We're still adopting Relay, so being forceful with respect to best practices is important to us.

We anticipate that this pattern will occur somewhat frequently for us.

What we would like feedback on

  • Would an ignore: RegExp parameter to the rule be an accepted addition? Namely, we would like to opt out of this lint rule by having certain inline fragments contain _inline_ in their name. (We would pair this with an additional rule, probably Pinterest-only, that enforced that only @inline fragments can contain _inline_ in their name.)

Support type aliases

Apparently code like below triggers the rules because the type alias isn't followed:

import type {
   HiringProfileRecruitingEmail_email as Email
} from 'HiringProfileRecruitingEmail_email.graphql';

generated-flow-types doesn't report invalid imports

Hello, my colleague discovered that incorrect Relay imports are not reported as expected. Failing test (well, should be failing but it's not):

{
  code: `
    import type {invalid as MyComponent_user} from './__generated__/MyComponent_user.graphql'
    class MyComponent extends React.Component<{user: MyComponent_user}> {
      render() {
        return <div />;
      }
    }

    createFragmentContainer(MyComponent, {
      user: graphql\`fragment MyComponent_user on User {id}\`,
    });
  `,
  errors: [
    {
      message: 'TODO: invalid type imported'
    }
  ]
},

Notice that the imported type invalid as MyComponent_user doesn't exist. In fact, this rule doesn't require the import at all and it is happy even if you delete the import type line completely. Seems like it takes into account only the property name used in props definition but not the imports. I believe this is not intended and it's a bug because seems like there are tests to cover this but they don't work as I would expect - the import is irrelevant for them:

import type {MyComponent_user} from './__generated__/MyComponent_user.graphql'
class MyComponent extends React.Component<{user: MyComponent_user}> {

cc @kassens, thanks for checking it and confirming/rejecting it

Should using plugin:relay/{recommended,strict} should automatically reference the plugin?

I apologize for the indecision on whether this issue is a bug report or a support question. At the heart of it, my ESLint config consists of extends from several different plugins (e.g., eslint-plugin-react), none of which need an explicit reference in the plugins section. Based on that, I'd expect that if I use extends: ['plugin:relay/recommended'] that that would imply plugins: ['relay'], but it currently does not.

Before opening this issue, I tried to see if ESLint was specially handling these other plugins or if the plugins were violating some spec, but still working incidentally. Unfortunately, I couldn't find anything conclusive. The ESLint docs give examples where the plugins section is populated, but don't really say if that's a requirement. Interestingly, the eslint-plugin-react example config is different than the one in eslint-plugin-react's docs and says you don't need to populate plugins if using an extends ruleset.

If this functionality can't be added, then I think the docs could be enhanced. While I realize adding 'relay' to the plugins section is step 1 of "How to Use", it's paired with a step 2 that discusses activating individual rules. I misread the part about using "plugin:relay/recommended" as an alternative to both steps that preceded it, rather than just the most recent step. I was likely primed to misread that based on my experience with other ESLint plugins.

Spreading props causes issues with unused-fields

Passing fields into components by spreading shows them as unused by the relay/unused-fields, while passing them one by one does not cause any issues.

type Props = {
  data: ParentComponent_data;
};

const ParentComponent: FC<Props> = ({ data }) => {
  const { title, ...rest } = data;
  return <SharedComponent title={title} {...rest} />;
}

export default createFragmentContainer(ParentComponent, {
  data: graphql`
    fragment ParentComponent_data on Data {
       name # <- Triggers a warning about not being used!
       title
    }
  `,
});

must-colocate-fragment-spreads gives false errors with yarn workspaces

Hi, we have a monorepo setup with yarn workspaces, where we have 2 apps, and a workspace of shared fragments between these apps.

Importing import { MyComponent } from '@org/fragments triggers error from this rule, even if its fragment is correctly used in the importing component.

I tried having a look at the code and see if I could come up with a fix, but it seems like this rule is based on matching the ImportDeclaration value against the name of the fragment.

I am not sure how eslint can extract this from the workspace name.

Fixes should adhere to `artifactDirectory` configured in `relay.config.js`

Currently, you can switch between imports being auto fixed either:
haste: true => "<type>.graphql"
haste: false => "./__generated__/<type>.graphql".
But with artifactDirectory set it would probably need to be:

  • the relative path from the current directory to the artifactDirectory
  • or the absolute path from the baseUrl setting in tsconfig (which is not always possible, because either not set, or not a ts repository at all)

Breaking change on v0.0.28

v0.0.28 release has involved to update graphql dep from v0.13.0 to v14.0.0. The problem with the latter version it doesn't support node 9.x:

error [email protected]: The engine "node" is incompatible with this module. Expected version "6.x || 8.x || >= 10.x".

So, I think this evolution would have deserved at least a minor version.

Pass/Fail Examples for Each Rule?

I'm looking to use this plugin but it's unclear from the README what each rule does. Can you add an explanation of what each rule's purpose is and examples of what passing & failing code looks like? Thanks!

unused-fields rule don't respect typescript's typeof value

Hello,

I have the following typescript code:

export const supportedCurrenciesEnum = ['usd', 'eur', 'czk'] as const;
export type supportedCurrencyType = (typeof supportedCurrenciesEnum)[number];

// then in the fragment query i'm using this code:

fragment {
  salePercentage {
            czk
            usd
            eur
        }
} 

// then deep in the react code i'm trying to access the salePercentage value like this:
const currencyCode: supportedCurrencyType = 'czk';

salePercentage[currencyCode]

But the rule still complaining on the fields in the fragment:

This queries for the field `czk` but this file does not seem to use it directly. If a different file needs this information that file should export a fragment and colocate the query for the data with the usage.
If only interested in the existence of a record, __typename can be used without this warning.eslint(relay/unused-fields)

But when i access the salePercentage like this:

const value = currencyCode === 'czk' ? salePercentage.czk : currencyCode === 'eur' ? salePercentage.eur : salePercentage.usd;

It works.

Do you have please any idea how to keep the easy access and don't use the second approach without the eslint warning?

Thank you

id property of typeAnnotation of NullableTypeAnnotation is not always defined

STDERR
     ../eslint-plugin-relay/eslint-plugin-relay.js:330
          propTypeProperty.value.typeAnnotation.id.name === type

TypeError: Cannot read property 'name' of undefined
at validateObjectTypeAnnotation (../eslint-plugin-relay/eslint-plugin-relay.js:330:45)
at Program:exit.expectedTypes.forEach.type (../eslint-plugin-relay/eslint-plugin-relay.js:741:23)
at Array.forEach (native)
at EventEmitter.Program:exit (../eslint-plugin-relay/eslint-plugin-relay.js:687:25)
at emitOne (events.js:120:20)
at EventEmitter.emit (events.js:210:7)
at NodeEventGenerator.leaveNode (../eslint/lib/util/node-event-generator.js:49:22)

Concerning this bit of code here:

if (
    propTypeProperty.value.type === 'NullableTypeAnnotation' &&
    propTypeProperty.value.typeAnnotation.id.name === type
  ) {
    return true;
  }

relay/must-colocate-fragment-spreads not working as expected on union types

With the following code I'm getting issues on fragment spreads. I've tried shifting things about a bit but can't work out what the issue is. The code executes fine at runtime.

export const MessageSenderAvatar: FC<MessageSenderAvatarProps> = ({
  messageSenderAvatarRef = null,
}) => {
  const fragmentRef = useFragment(
    graphql`
      fragment MessageSenderAvatarFragment on Message {
        sender {
          __typename
          ... on User {
            ...UserAvatarFragment <--- ISSUE
          }
          ... on Bot {
            ...BotAvatarFragment <--- ISSUE
          }
        }
      }
    `,
    messageSenderAvatarRef
  );

  if (fragmentRef == null) {
    return null;
  }

  if (fragmentRef.sender.__typename === 'User') {
    return <UserAvatar userAvatarRef={fragmentRef.sender} />;
  }

  if (fragmentRef.sender.__typename === 'Bot') {
    return <BotAvatar botAvatarRef={fragmentRef.sender} />;
  }

  return <AnonymousUserAvatar />;
};

and UserAvatar such as

export const UserAvatar: FC<UserAvatarProps> = ({ userAvatarRef, size = AvatarSize.Size40 }) => {
  const { avatarUrlTemplate, displayName } = useFragment(
    graphql`
      fragment UserAvatarFragment on User {
        avatarUrlTemplate
        displayName
      }
    `,
    userAvatarRef
  );

  return (
    <AvatarView
      name={displayName}
      imageUrl={getScaledUrlTemplate(avatarUrlTemplate, size)}
      size={size}
    />
  );
};

Issue upgrading to 1.8.0

Hi ๐Ÿ‘‹

We are having a little issue with upgrading to the latest version.

Running lint locally fails with

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Node'
    |     property 'expressions' -> object with constructor 'Array'
    |     index 0 -> object with constructor 'Node'
    --- property 'parent' closes the circle
    at stringify (<anonymous>)

But on the CI it gives a different error. You can see the CI run here.

The error on the CI says The "path" argument must be of type string. Received undefined.

This is the line that causes the error. Changing it from import(..) to require(..) solves it locally, but I expect it should work for import as well

How do we use this?

Nothing explaining in the Readme.md about how to get this to work or any examples.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.