Files
nrwl--nx/eslint.config.mjs
T
2026-07-13 12:38:36 +08:00

219 lines
6.6 KiB
JavaScript

import nx from '@nx/eslint-plugin';
import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin';
import typescriptEslintParser from '@typescript-eslint/parser';
import globals from 'globals';
import jest from 'eslint-plugin-jest';
import * as jsoncEslintParser from 'jsonc-eslint-parser';
import storybook from 'eslint-plugin-storybook';
import toolsEslintRulesRawFileParserJs from './tools/eslint-rules/raw-file-parser.js';
const testFiles = [
'**/*.spec.ts',
'**/*.spec.tsx',
'**/*.spec.js',
'**/*.spec.jsx',
'**/*.test.ts',
'**/*.test.tsx',
'**/*.test.js',
'**/*.test.jsx',
];
// eslint-plugin-storybook's flat/recommended references rules from react-hooks
// and import-x (not registered in this repo). Keep only storybook-owned rules
// to avoid "plugin not found" validation errors.
const storybookConfigs = storybook.configs['flat/recommended'].map((config) => {
if (!config.rules) return config;
const ownRules = Object.fromEntries(
Object.entries(config.rules).filter(([name]) =>
name.startsWith('storybook/')
)
);
return { ...config, rules: ownRules };
});
// Shared base consumed by every subproject via the named export below.
// IMPORTANT: subprojects import `{ baseConfig }`, not the default, so they
// don't inherit the root's "ignore everything" safety net.
export const baseConfig = [
{ ignores: ['**/dist', '**/out-tsc', '**/test-output'] },
...storybookConfigs,
...nx.configs['flat/base'],
{ plugins: { '@typescript-eslint': typescriptEslintEslintPlugin } },
{
languageOptions: {
parser: typescriptEslintParser,
globals: { ...globals.node },
},
},
{
rules: {
'@typescript-eslint/explicit-module-boundary-types': 'off',
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'create-nx-workspace',
message: 'Please import utils from nx or @nx/devkit instead.',
},
{
name: 'node-fetch',
message:
"Please default to native fetch instead of 'node-fetch'.",
},
],
},
],
'@typescript-eslint/no-restricted-imports': [
'error',
{
patterns: [
{
group: ['nx/src/plugins/js*'],
message:
"Imports from 'nx/src/plugins/js' are not allowed. Use '@nx/js' instead",
},
{
group: ['**/native-bindings', '**/native-bindings.js', ''],
message:
'Direct imports from native-bindings.js are not allowed. Import from index.js instead.',
},
],
},
],
},
},
{
files: ['.storybook/**/main.@(js|cjs|mjs|ts)'],
rules: {
'storybook/no-uninstalled-addons': [
'error',
{
ignore: ['@nx/react/plugins/storybook'],
packageJsonLocation: '../../package.json',
},
],
},
},
{
files: ['**/*.json'],
languageOptions: {
parser: jsoncEslintParser,
},
},
{
files: ['**/executors/**/schema.json', '**/generators/**/schema.json'],
rules: {
'@nx/workspace-valid-schema-description': 'error',
},
},
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
checkDynamicDependenciesExceptions: ['.*'],
allow: [],
// These cycles are intentional: plugin packages declare graph
// edges to their lazy-loaded (ensurePackage) peers via
// `implicitDependencies` in project.json. The project graph is
// cyclic by design; the task graph is kept acyclic via explicit
// `dependsOn` overrides on the relevant build-base targets.
ignoredCircularDependencies: [
['js', 'workspace'],
['angular', 'workspace'],
['express', 'node'],
['nest', 'node'],
['nuxt', 'vue'],
],
depConstraints: [
{
sourceTag: '*',
onlyDependOnLibsWithTags: ['*'],
},
],
},
],
'@nx/workspace-valid-command-object': 'error',
'@nx/workspace-require-windows-hide': 'error',
},
},
{
files: ['pnpm-lock.yaml'],
rules: {
'@nx/workspace-ensure-pnpm-lock-version': [
'error',
{
version: '9.0',
},
],
},
languageOptions: {
parser: toolsEslintRulesRawFileParserJs,
},
},
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/prefer-standalone': 'off',
},
},
{
files: testFiles,
plugins: { jest },
rules: {
'jest/no-disabled-tests': 'warn',
},
},
];
// eslint-plugin-react-hooks@7 introduced stricter rules (set-state-in-effect,
// purity, immutability, etc.) that flag pre-existing patterns across the
// nx-dev / graph React apps. These are pulled in by `nx.configs['flat/react']`
// so spreading them here in baseConfig would be overridden by leaves that
// spread flat/react later. Leaves that use flat/react should spread this after
// it. Drop this override once the violations are fixed in a follow-up PR.
export const reactHooksV7Off = [
{
rules: {
'react-hooks/set-state-in-effect': 'off',
'react-hooks/purity': 'off',
'react-hooks/immutability': 'off',
'react-hooks/refs': 'off',
'react-hooks/preserve-manual-memoization': 'off',
'react-hooks/unsupported-syntax': 'off',
'react-hooks/static-components': 'off',
'react-hooks/config': 'off',
'react-hooks/incompatible-library': 'off',
'react-hooks/gating': 'off',
'react-hooks/error-boundaries': 'off',
'react-hooks/globals': 'off',
},
},
];
// e2e/* subprojects legacy-only linted test files (via ignorePatterns with
// `!**/*.test.ts` negation). Replicate the scope in flat config so each e2e
// leaf just spreads this.
export const e2eTestOnlyIgnores = {
ignores: [
'**/*.ts',
'**/*.tsx',
'**/*.js',
'**/*.jsx',
'!**/*.test.ts',
'!**/*.test.tsx',
'!**/*.spec.ts',
'!**/*.spec.tsx',
],
};
// Default export: for workspace-root lint invocations only. Subprojects have
// their own eslint.config.mjs (nearest-ancestor resolution) and never see this.
// Ignore everything except root-scoped files that should be linted at workspace
// level (currently just pnpm-lock.yaml via the @nx/workspace-ensure-pnpm-lock
// rule); the root isn't meant to lint subprojects directly.
export default [...baseConfig, { ignores: ['**/*', '!pnpm-lock.yaml'] }];