CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nx--eslint-plugin

ESLint plugin for Nx monorepos with boundary enforcement and dependency management rules.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

workspace-rules.mddocs/

Workspace Rule Integration

The @nx/eslint-plugin provides dynamic loading and integration of custom ESLint rules from the workspace's tools/eslint-rules directory, enabling project-specific linting extensions while maintaining proper namespacing and TypeScript support.

Capabilities

Workspace Rules Loading

Automatically discovers and loads custom ESLint rules from the workspace tools directory.

/**
 * Dynamically loaded workspace rules with automatic namespacing
 */
interface WorkspaceRules {
  /** Current namespacing format */
  [key: `workspace-${string}`]: TSESLint.RuleModule<string, unknown[]>;
  
  /** Legacy namespacing format for backwards compatibility */
  [key: `workspace/${string}`]: TSESLint.RuleModule<string, unknown[]>;
}

/**
 * Function that loads workspace rules on plugin initialization
 */
function loadWorkspaceRules(): WorkspaceRules;

Workspace Plugin Structure

Expected structure for custom workspace rules.

// tools/eslint-rules/index.ts
interface WorkspacePlugin {
  rules: {
    [ruleName: string]: TSESLint.RuleModule<string, unknown[]>;
  };
}

// Example workspace plugin
export const rules = {
  "no-barrel-imports": noBarrelImportsRule,
  "enforce-naming-convention": enforceNamingConventionRule,
  "restrict-third-party-libs": restrictThirdPartyLibsRule
};

Rule Namespacing

All workspace rules are automatically namespaced to prevent conflicts with official rules.

interface RuleNamespacing {
  /** Original rule name in workspace */
  originalName: string;
  
  /** Current namespaced format */
  namespacedName: `workspace-${string}`;
  
  /** Legacy namespaced format (backwards compatibility) */
  legacyNamespacedName: `workspace/${string}`;
}

// Example transformations:
// "no-barrel-imports" → "workspace-no-barrel-imports" 
// "no-barrel-imports" → "workspace/no-barrel-imports" (legacy)

Constants and Configuration

Key constants used for workspace rule integration.

/** Path to workspace rules directory relative to workspace root */
const WORKSPACE_RULES_PATH: "tools/eslint-rules";

/** Full path to workspace plugin directory */  
const WORKSPACE_PLUGIN_DIR: string; // join(workspaceRoot, WORKSPACE_RULES_PATH)

/** Namespace prefix for workspace rules */
const WORKSPACE_RULE_PREFIX: "workspace";

Usage Examples:

// Creating custom workspace rule
// tools/eslint-rules/rules/no-barrel-imports.ts
import { ESLintUtils } from '@typescript-eslint/utils';

export default ESLintUtils.RuleCreator(() => '')({
  name: 'no-barrel-imports',
  meta: {
    type: 'problem',
    docs: {
      description: 'Disallow barrel exports that re-export everything'
    },
    messages: {
      noBarrelImports: 'Avoid barrel imports that re-export everything'
    },
    schema: []
  },
  defaultOptions: [],
  create(context) {
    return {
      ExportAllDeclaration(node) {
        context.report({
          node,
          messageId: 'noBarrelImports'
        });
      }
    };
  }
});

// tools/eslint-rules/index.ts
import noBarrelImports from './rules/no-barrel-imports';

export const rules = {
  'no-barrel-imports': noBarrelImports
};

// Using workspace rule in ESLint config
module.exports = {
  rules: {
    '@nx/workspace-no-barrel-imports': 'error'
  }
};

// Flat config usage
import nxPlugin from '@nx/eslint-plugin';

export default [
  {
    plugins: {
      '@nx': nxPlugin
    },
    rules: {
      '@nx/workspace-no-barrel-imports': 'error'
    }
  }
];

TypeScript Integration

Workspace rules support full TypeScript compilation and type checking.

interface TypeScriptIntegration {
  /** TypeScript configuration for workspace rules */
  tsConfigPath: string; // tools/eslint-rules/tsconfig.json
  
  /** Automatic TypeScript project registration */
  projectRegistration: () => (() => void) | undefined;
  
  /** Compilation and loading process */
  compilationSupport: boolean;
}

// Example tsconfig.json for workspace rules
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2020",
    "lib": ["es2020"],
    "declaration": false,
    "strict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["**/*.ts"],
  "exclude": ["**/*.spec.ts"]
}

Error Handling and Fallbacks

Robust error handling ensures the plugin continues to work even when workspace rules fail to load.

interface ErrorHandling {
  /** Fallback when tools/eslint-rules doesn't exist */
  missingDirectory: () => WorkspaceRules; // Returns {}
  
  /** Fallback when loading fails */
  loadingError: (error: Error) => WorkspaceRules; // Logs error, returns {}
  
  /** Cleanup function for TypeScript registration */
  cleanup: () => void;
}

Usage Examples:

// Complex workspace rule with options
// tools/eslint-rules/rules/enforce-project-structure.ts
import { ESLintUtils } from '@typescript-eslint/utils';

interface Options {
  allowedDirectories: string[];
  bannedPatterns: string[];
}

export default ESLintUtils.RuleCreator(() => '')({
  name: 'enforce-project-structure',
  meta: {
    type: 'problem',
    docs: {
      description: 'Enforce project structure conventions'
    },
    schema: [{
      type: 'object',
      properties: {
        allowedDirectories: {
          type: 'array',
          items: { type: 'string' }
        },
        bannedPatterns: {
          type: 'array', 
          items: { type: 'string' }
        }
      },
      additionalProperties: false
    }],
    messages: {
      invalidDirectory: 'Files in {{directory}} are not allowed',
      bannedPattern: 'File matches banned pattern: {{pattern}}'
    }
  },
  defaultOptions: [{
    allowedDirectories: ['src', 'lib'],
    bannedPatterns: []
  }],
  create(context, [options]) {
    return {
      Program(node) {
        const filename = context.getFilename();
        // Implementation logic
      }
    };
  }
});

// Using in ESLint config with options
module.exports = {
  rules: {
    '@nx/workspace-enforce-project-structure': [
      'error',
      {
        allowedDirectories: ['src', 'lib', 'test'],
        bannedPatterns: ['**/legacy/**', '**/*.old.*']
      }
    ]
  }
};

Integration with Nx Plugin System

Workspace rules integrate seamlessly with Nx's broader plugin ecosystem:

  • Project Graph Access: Rules can access the Nx project graph for workspace-aware linting
  • Configuration Integration: Rules can read Nx project configurations
  • Build System Integration: Rules can integrate with Nx build targets and caching
  • Migration Support: Rules can participate in Nx migration workflows

Best Practices for Workspace Rules

Guidelines for creating effective workspace-specific rules:

  1. Namespace Carefully: Use descriptive names that won't conflict with future official rules
  2. TypeScript First: Write rules in TypeScript for better maintainability
  3. Error Handling: Provide clear error messages and handle edge cases
  4. Performance: Consider performance impact on large workspaces
  5. Testing: Include comprehensive tests for workspace rules
  6. Documentation: Document rule purpose and configuration options

Backwards Compatibility

The plugin maintains backwards compatibility for existing workspace rule configurations:

// Both formats work:
module.exports = {
  rules: {
    '@nx/workspace-my-rule': 'error',        // Current format
    '@nx/workspace/my-rule': 'error'         // Legacy format (still supported)
  }
};

Install with Tessl CLI

npx tessl i tessl/npm-nx--eslint-plugin

docs

configurations.md

dependency-checks.md

index.md

module-boundaries.md

plugin-validation.md

workspace-rules.md

tile.json