npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-pimsical-shopify-graphql

v0.0.4

Published

An ESLint plugin to help check Shopify GraphQL queries follow best practice. Built by a Shopify Partner.

Readme

eslint-plugin-pimsical-shopify-graphql

This is an ESLint plugin to help check Shopify GraphQL queries follow best practice. Built by Pimsical, a Shopify Partner.

It is designed to work alongside @graphql-eslint/eslint-plugin

TLDR;

Installation

npm i -D eslint-plugin-pimsical-shopify-graphql @graphql-eslint/eslint-plugin

Usage

You need to be using eslint flat config files, then we will need to add some config to your eslint.config.js.

There are two ways to use this plugin, depending on where your GraphQL queries are located.

Queries in code

If you have GraphQL queries inside TypeScript or JavaScript files, you can use the graphql-eslint processor to allow us to lint them.

Here is an example for a TypeScript project like so (or change the file extensions to match your project):

import graphqlPlugin from '@graphql-eslint/eslint-plugin'
import shopifyGraphqlPlugin from 'eslint-plugin-pimsical-shopify-graphql';

export default [
  {
    files: ['**/*.ts'],
    processor: graphqlPlugin.processor
  },
  {
    files: ['**/*.graphql'],
    languageOptions: {
      parser: graphqlPlugin.parser
    },
    plugins: {
      '@graphql-eslint': graphqlPlugin,
      'pimsical-shopify-graphql': shopifyGraphqlPlugin,
    },
    rules: {
      ...graphqlPlugin.configs['flat/operations-recommended'].rules,
      '@graphql-eslint/require-selections': 'off',
      ...shopifyGraphqlPlugin.configs['flat/recommended'].rules,
    },
  },
];

The graphql-eslint processor will pick up any GraphQL queries inside your TypeScript or JavaScript files, and convert them to .graphql files in memory only allowing us to lint them.

It will pick up any queries inside gql or graphql tagged template literals, or beginning with the comment /* GraphQL */ before the query by default.

To make the tools work more seamlessly and improve linting we recommend integrating with Shopify GraphQL Codegen below.

Queries in .graphql files

If you have GraphQL queries are in .graphql files already, you don't need the processor so your config can work like so:

import graphqlPlugin from '@graphql-eslint/eslint-plugin'
import shopifyGraphqlPlugin from 'eslint-plugin-pimsical-shopify-graphql';

export default [
  {
    files: ['**/*.graphql'],
    languageOptions: {
      parser: graphqlPlugin.parser
    },
    plugins: {
      '@graphql-eslint': graphqlPlugin,
      'pimsical-shopify-graphql': shopifyGraphqlPlugin,
    },
    rules: {
      ...graphqlPlugin.configs['flat/operations-recommended'].rules,
      '@graphql-eslint/require-selections': 'off',
      ...shopifyGraphqlPlugin.configs['flat/recommended'].rules,
    },
  },
];

Shopify GraphQL Schema

The linters work best combined with knowledge about Shopify GraphQL Schema, there are two ways to configure this:

If you are already using Shopify GraphQL Codegen, this does a lot of the work for us and is our recommended approach.

In your .graphqlrc.ts or equivalent JS file, ensure that pluckConfig is exposed in the default extensions object.

This will allow the linters to pick up the GraphQL code from your codebase using the same prefix as the codegen tools, it also uses the schemas from here as well.

import { shopifyApiProject, ApiType } from '@shopify/api-codegen-preset';

const apiVersion = '2025-04';
const config = shopifyApiProject({
  apiType: ApiType.Admin,
  apiVersion: apiVersion,
  documents: ['./**/*.{js,ts,jsx,tsx}'],
  outputDir: './shopify/types',
})

export default {
  schema: `https://shopify.dev/admin-graphql-direct-proxy/${apiVersion}`,
  documents: ['./**/*.{js,ts,jsx,tsx}'],
  projects: {
    default: {
      ...config,
      extensions: {
        ...config.extensions,
        pluckConfig: config.extensions?.codegen?.pluckConfig,
      },
    },
  },
};

Manually defined schema

If you are not using the codegen tools, you can manually define the schema in your eslint.config.js under parserOptions or add an equivalent Graphql Config file which will automatically get picked up.

import js from '@eslint/js';
import graphqlPlugin from '@graphql-eslint/eslint-plugin';

export default [
  {
    files: ['**/*.graphql'],
    languageOptions: {
      parser: graphqlPlugin.parser,
      parserOptions: {
        graphQLConfig: {
          schema: `https://shopify.dev/admin-graphql-direct-proxy/2025-04`,
          documents: [['./**/*.{js,ts,jsx,tsx,graphql}']],
        },
      },
    },
    plugins: {
      '@graphql-eslint': graphqlPlugin,
      'pimsical-shopify-graphql': shopifyGraphqlPlugin,
    },
    rules: {
      ...graphqlPlugin.configs['flat/operations-recommended'].rules,
      '@graphql-eslint/require-selections': 'off',
      ...shopifyGraphqlPlugin.configs['flat/recommended'].rules,
    },
  },
];

Rules

pimsical-shopify-graphql/max-first-argument

This rule checks that the first argument of a query is not greater than 250. This is to ensure that queries adhere to Shopify's maximum page size.

Examples

Correct
query getVariants($cursor: String) {
  productVariants(first: 100, after: $cursor) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
      }
    }
  }
}
Incorrect
query getVariants($cursor: String) {
  productVariants(first: 300, after: $cursor) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
      }
    }
  }
}

pimsical-shopify-graphql/require-mutation-user-errors

This rule checks that any mutations contain userErrors, this is how Shopify returns errors from your mutation input.

Examples

Correct
mutation inventoryAdjustQuantities($input: InventoryAdjustQuantitiesInput!) {
  inventoryAdjustQuantities(input: $input) {
    userErrors {
      field
      message
    }
    inventoryAdjustmentGroup {
      createdAt
      reason
    }
  }
}
Incorrect
mutation inventoryAdjustQuantities($input: InventoryAdjustQuantitiesInput!) {
  inventoryAdjustQuantities(input: $input) {
    inventoryAdjustmentGroup {
      createdAt
      reason
    }
  }
}

pimsical-shopify-graphql/require-query-page-info

This rule checks that if first or after arguments are used in a query, it also has a pageInfo block so you can check if there are more pages or paginate through the results.

Examples

Correct
query getVariants($cursor: String) {
  productVariants(first: 100, after: $cursor) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
      }
    }
  }
}
Incorrect
query getVariants($cursor: String) {
  productVariants(first: 300, after: $cursor) {
    edges {
      node {
        id
      }
    }
  }
}

License

MIT