-
Notifications
You must be signed in to change notification settings - Fork 3k
Expand file tree
/
Copy patheslint.config.mjs
More file actions
266 lines (257 loc) · 9.24 KB
/
Copy patheslint.config.mjs
File metadata and controls
266 lines (257 loc) · 9.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
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 };
});
// Patterns shared between the base rule and the allowDirectNxImports
// escape hatch below.
const restrictedImportPatterns = [
{
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.',
},
];
// Published packages must not import 'nx' directly — '@nx/devkit' (or
// '@nx/devkit/internal' for first-party-only utilities) is the compatibility
// boundary between plugin majors and nx majors. See packages/devkit/CLAUDE.md.
// packages/nx and packages/devkit redefine this rule in their own configs.
//
// Anchored regex rather than a `['nx', 'nx/**']` glob: eslint matches import
// groups with gitignore semantics, so the slash-less `nx` entry needed to catch
// the bare specifier is unanchored and also matches any local path with an `nx`
// segment (e.g. create-nx-workspace's `./utils/nx/nx-cloud`). `nx/**` itself is
// fine — it contains a slash, so it is anchored. The regex matches only the real
// package — `nx` and `nx/…` — while leaving relative paths and `@nx/*` alone.
//
// `nx/release` is exempt: it is a public, stable entry point whose runtime API
// (VersionActions, releasePublish, releaseVersion) transitively pulls in the
// project graph, so routing it through the devkit barrels — which plugin graph
// hooks load in every plugin worker — would add ~73 modules to the closure they
// eagerly load. Release-extension plugins import it directly.
const directNxImportPattern = {
regex: '^nx($|/(?!release($|/)))',
message:
"Import from '@nx/devkit' or '@nx/devkit/internal' instead of 'nx' — devkit is the version-compatibility boundary (see packages/devkit/CLAUDE.md).",
};
// Escape hatch for non-published projects (e2e suites, the graph client)
// that legitimately reach into nx internals. Append AFTER baseConfig.
//
// WARNING: this matches every linted file by default. Flat config *replaces* a
// rule's options rather than merging them, so appending this after a config that
// redefines `@typescript-eslint/no-restricted-imports` for a narrower set of
// files silently discards that narrower configuration. The failure is invisible
// in CI, because the result is *fewer* lint errors, not more. Narrow it by
// overriding `files` AFTER the spread — `{ ...allowDirectNxImports, files:
// ['**/*.spec.ts'] }` — in any project that defines its own
// `no-restricted-imports` overrides (see packages/nx/eslint.config.mjs).
export const allowDirectNxImports = {
files: ['**/*'],
rules: {
'@typescript-eslint/no-restricted-imports': [
'error',
{ patterns: restrictedImportPatterns },
],
},
};
// 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: [...restrictedImportPatterns, directNxImportPattern] },
],
},
},
{
files: ['.storybook/**/main.@(js|cjs|mjs|ts)'],
rules: {
'storybook/no-uninstalled-addons': [
'error',
{
ignore: ['@nx/react/plugins/storybook'],
packageJsonLocation: '../../package.json',
},
],
},
},
{
files: ['**/executors/**/schema.json', '**/generators/**/schema.json'],
languageOptions: {
parser: jsoncEslintParser,
},
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 or, for `js`, devDependencies in package.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'],
['js', '@nx/oxlint'],
['angular', 'workspace'],
['express', 'node'],
['nest', 'node'],
['nuxt', 'vue'],
],
depConstraints: [
{
sourceTag: '*',
onlyDependOnLibsWithTags: ['*'],
},
],
},
],
'@nx/workspace-valid-command-object': 'error',
'@nx/workspace-require-windows-hide': 'error',
// allowSeparateTypeImports keeps `import type` declarations distinct from
// value ones; collapsing that split would churn ~226 files.
'no-duplicate-imports': ['error', { allowSeparateTypeImports: true }],
},
},
{
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'] }];