console/.eslintrc.cjs

333 lines
12 KiB
JavaScript

/* eslint-env node */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const guildConfig = require('@theguild/eslint-config/base');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { REACT_RESTRICTED_SYNTAX, RESTRICTED_SYNTAX } = require('@theguild/eslint-config/constants');
const path = require('path');
const SCHEMA_PATH = './packages/services/api/src/modules/*/module.graphql.ts';
const OPERATIONS_PATHS = [
'./packages/web/app/**/*.ts',
'./packages/web/app/**/*.tsx',
'./packages/web/app/**/*.graphql',
];
const rulesToExtends = Object.fromEntries(
Object.entries(guildConfig.rules).filter(([key]) =>
[
'import/first',
'no-restricted-globals',
'@typescript-eslint/no-unused-vars',
'unicorn/no-array-push-push',
'no-else-return',
'no-lonely-if',
'unicorn/prefer-includes',
'no-extra-boolean-cast',
].includes(key),
),
);
const HIVE_RESTRICTED_SYNTAX = [
{
// ❌ '0.0.0.0' or `0.0.0.0`
selector: ':matches(Literal[value="0.0.0.0"], TemplateElement[value.raw="0.0.0.0"])',
message: 'Use "::" to make it compatible with both IPv4 and IPv6',
},
];
const tailwindCallees = ['clsx', 'cn', 'cva', 'cx'];
/**
* @type {import('eslint').Linter.Config}
*/
module.exports = {
ignorePatterns: [
'scripts',
'rules',
'out',
'.hive',
'packages/web/app/.ladle/**',
'public',
'packages/web/app/src/graphql/index.ts',
'packages/libraries/cli/src/sdk.ts',
'packages/libraries/cli/src/gql/**/*',
'packages/services/storage/src/db/types.ts',
'packages/web/app/src/gql/**/*',
'codegen.cjs',
'tsup',
'packages/libraries/render-laboratory/src/laboratory.ts',
'packages/web/app/vite.config.ts',
],
overrides: [
{
// Setup GraphQL Parser
files: '*.{graphql,gql}',
parser: '@graphql-eslint/eslint-plugin',
plugins: ['@graphql-eslint'],
parserOptions: {
schema: SCHEMA_PATH,
operations: OPERATIONS_PATHS,
},
rules: {
'@graphql-eslint/no-deprecated': 'error',
'@graphql-eslint/require-id-when-available': 'error',
},
},
{
// Setup processor for operations/fragments definitions on code-files
files: ['packages/web/app/**/*.tsx', 'packages/web/app/**/*.ts'],
processor: '@graphql-eslint/graphql',
},
{
files: ['packages/web/app/**/*.graphql'],
plugins: ['@graphql-eslint'],
rules: {
'@graphql-eslint/no-deprecated': 'error',
'@graphql-eslint/require-id-when-available': 'error',
},
},
{
files: ['*.cjs'],
parserOptions: { ecmaVersion: 2020 },
},
{
files: ['packages/**/*.ts', 'packages/**/*.tsx', 'cypress/**/*.ts', 'cypress/**/*.tsx'],
reportUnusedDisableDirectives: true,
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
project: [path.join(__dirname, './tsconfig.eslint.json')],
},
parser: '@typescript-eslint/parser',
plugins: [...guildConfig.plugins, 'hive'],
extends: guildConfig.extends,
rules: {
'no-process-env': 'error',
'no-empty': ['error', { allowEmptyCatch: true }],
'import/no-absolute-path': 'error',
'import/no-self-import': 'error',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'packages/services/storage/tools/*.js',
'packages/services/**',
'packages/migrations/**',
// We bundle it all anyway, so there are no node_modules
'packages/web/app/**',
// We bundle it all anyway, so there are no node_modules
'packages/libraries/laboratory/**',
'**/*.spec.ts',
'**/*.test.ts',
'**/*.e2e.ts',
],
optionalDependencies: false,
},
],
'hive/enforce-deps-in-dev': [
'error',
{
scopes: ['@hive', '@graphql-hive'],
ignored: ['packages/libraries/**', 'packages/web/**'],
},
],
'@typescript-eslint/no-floating-promises': 'error',
'sonarjs/no-unused-collection': 'warn',
'sonarjs/no-inverted-boolean-check': 'warn',
...rulesToExtends,
'no-lonely-if': 'off',
'object-shorthand': 'off',
'no-restricted-syntax': ['error', ...HIVE_RESTRICTED_SYNTAX, ...RESTRICTED_SYNTAX],
'prefer-destructuring': 'off',
'prefer-const': 'off',
'no-useless-escape': 'off',
'no-inner-declarations': 'off',
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/triple-slash-reference': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
caughtErrors: 'none',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
},
},
{
files: ['packages/web/**'],
extends: [
'@theguild',
'@theguild/eslint-config/react',
'plugin:better-tailwindcss/legacy-recommended',
'plugin:@next/next/recommended',
],
settings: {
'import/resolver': {
typescript: {
project: ['packages/web/app/tsconfig.json'],
},
},
},
rules: {
// conflicts with official prettier-plugin-tailwindcss and tailwind v3
'better-tailwindcss/enforce-consistent-class-order': 'off',
'better-tailwindcss/enforce-canonical-classes': 'off',
// keeping classes in one line helps prettier-plugin-tailwindcss
// enable wrapping in text editors to make classes human readable
'better-tailwindcss/enforce-consistent-line-wrapping': 'off',
'better-tailwindcss/enforce-shorthand-classes': 'off',
'react/display-name': 'off',
'react/prop-types': 'off',
'react/no-unknown-property': 'off',
'jsx-a11y/anchor-is-valid': ['off', { components: ['Link', 'NextLink'] }],
'jsx-a11y/alt-text': ['warn', { elements: ['img'], img: ['Image', 'NextImage'] }],
'no-restricted-syntax': ['error', ...HIVE_RESTRICTED_SYNTAX, ...REACT_RESTRICTED_SYNTAX],
'prefer-destructuring': 'off',
'no-console': 'off',
'no-useless-escape': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'react/jsx-no-useless-fragment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'off',
'unicorn/filename-case': 'off',
'import/no-default-export': 'off',
'@next/next/no-img-element': 'off',
'@typescript-eslint/ban-types': 'off',
'jsx-a11y/label-has-associated-control': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/no-static-element-interactions': 'off',
'@next/next/no-html-link-for-pages': 'off',
'unicorn/no-negated-condition': 'off',
'no-implicit-coercion': 'off',
'react/jsx-key': 'warn',
},
},
{
files: ['packages/web/app/**'],
settings: {
'better-tailwindcss': {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: 'packages/web/app/src/index.css',
callees: tailwindCallees,
},
},
rules: {
// better-tailwindcss assumes you're using v4...we're being explicit here due to our dual tailwind setups
'better-tailwindcss/no-deprecated-classes': 'error',
// Tailwind v4 uses CSS variables without var() syntax
'better-tailwindcss/enforce-consistent-variable-syntax': 'off',
'better-tailwindcss/no-unknown-classes': [
'error',
{
ignore: [
'drag-none',
// Animation utilities (from index.css, replaces tailwindcss-animate)
'animate-in',
'animate-out',
'fade-in-.*',
'fade-out-.*',
'zoom-in-.*',
'zoom-out-.*',
'slide-in-from-.*',
'slide-out-to-.*',
// Custom radius from @theme
'rounded-xs',
// ring-offset with semantic colors
'ring-offset-.*',
// Data attribute variants with custom animations (for Radix UI components)
'data-\\[side=(top|right|bottom|left)\\]:animate-slide-(up|down|left|right)-fade',
// GraphiQL classes
'graphiql-.*',
// hive classes
'hive-.*',
// Schema diff custom classes (defined in index.css)
'schema-doc-row-.*',
// No scrollbar utility (defined in index.css)
'no-scrollbar',
// Tailwind v4 CSS variable syntax with parentheses
'.*-\\(--.*\\)',
],
},
],
},
},
{
files: ['packages/web/app/**/*.stories.tsx', 'packages/web/docs/**'],
rules: {
'react-hooks/rules-of-hooks': 'off',
},
},
{
files: ['packages/web/docs/**'],
settings: {
next: {
rootDir: 'packages/web/docs',
},
'better-tailwindcss': {
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: 'packages/web/docs/tailwind.config.ts',
callees: tailwindCallees,
},
},
rules: {
'import/extensions': 'off',
// better-tailwindcss assumes you're using v4...we're being explicit here due to our dual tailwind setups
'better-tailwindcss/no-deprecated-classes': 'off',
'better-tailwindcss/no-unknown-classes': [
'error',
{
ignore: [
'light',
'hive-focus',
'hive-focus-within',
'nextra-focus',
'nextra-scrollbar',
'no-scrollbar', // from Nextra
'hive-slider',
'hive-prose',
'subheader',
'subheading-anchor',
'duration-\\[.*\\]', // Allow arbitrary duration values like duration-[.8s]
'ease-\\[var\\(--.*\\)\\]', // Allow CSS variables in arbitrary ease values
'x:.*', // Allow Nextra 4 custom variant prefix
],
},
],
// Allow CSS variables in arbitrary values for Tailwind v3
'better-tailwindcss/enforce-consistent-variable-syntax': 'off',
},
},
{
files: 'cypress/**',
extends: 'plugin:cypress/recommended',
rules: {
'cypress/no-unnecessary-waiting': 'off',
'cypress/unsafe-to-chain-command': 'off',
},
},
{
files: [
// environment should be parsed to avoid global dependencies and sacred .env files
'packages/**/environment.ts',
// - environment is inlined and must be "registered" in next.config.js
// - `import.meta.env` is not supported in Next.js yet
'packages/web/docs/**',
],
rules: {
'no-process-env': 'off',
},
},
],
};