1 | /*!
|
2 | * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
3 | * Licensed under the MIT License.
|
4 | */
|
5 |
|
6 | /**
|
7 | * Shared list of permitted imports for configuring and override the `import/no-internal-modules` rule.
|
8 | */
|
9 | const permittedImports = [
|
10 | // Within Fluid Framework allow import of '/internal' from other FF packages.
|
11 | "@fluid-example/*/internal",
|
12 | "@fluid-experimental/*/internal",
|
13 | "@fluid-internal/*/internal",
|
14 | "@fluid-private/*/internal",
|
15 | "@fluid-tools/*/internal",
|
16 | "@fluidframework/*/internal",
|
17 |
|
18 | // Experimental package APIs and exports are unknown, so allow any imports from them.
|
19 | "@fluid-experimental/**",
|
20 |
|
21 | // Allow imports from sibling and ancestral sibling directories,
|
22 | // but not from cousin directories. Parent is allowed but only
|
23 | // because there isn't a known way to deny it.
|
24 | "*/index.js",
|
25 | ];
|
26 |
|
27 | /**
|
28 | * "Minimal" eslint configuration.
|
29 | *
|
30 | * This configuration is primarily intended for use in packages during prototyping / initial setup.
|
31 | * Ideally, all of packages in the fluid-framework repository should derive from either the "Recommended" or
|
32 | * "Strict" configuration.
|
33 | *
|
34 | * Production packages **should not** use this configuration.
|
35 | *
|
36 | * @deprecated This config is too permissive and should not be used. It will be removed in a future release.
|
37 | * Use the "Recommended" or "Strict" configuration instead.
|
38 | *
|
39 | * @privateRemarks TODO: Once this config is ready for deletion, its rules can be moved into `recommended.js`.
|
40 | */
|
41 | module.exports = {
|
42 | env: {
|
43 | browser: true,
|
44 | es6: true,
|
45 | es2024: false,
|
46 | node: true,
|
47 | },
|
48 | extends: [
|
49 | "./base",
|
50 | "plugin:eslint-comments/recommended",
|
51 | "plugin:import/errors",
|
52 | "plugin:import/warnings",
|
53 | "plugin:import/typescript",
|
54 | "prettier",
|
55 | ],
|
56 | globals: {
|
57 | Atomics: "readonly",
|
58 | SharedArrayBuffer: "readonly",
|
59 | },
|
60 | parser: "@typescript-eslint/parser",
|
61 | parserOptions: {
|
62 | ecmaFeatures: {
|
63 | jsx: true,
|
64 | },
|
65 | ecmaVersion: 2018,
|
66 | sourceType: "module",
|
67 | project: "./tsconfig.json",
|
68 | },
|
69 | plugins: [
|
70 | // Plugin documentation: https://www.npmjs.com/package/@rushstack/eslint-plugin
|
71 | "@rushstack/eslint-plugin",
|
72 | // Plugin documentation: https://www.npmjs.com/package/@rushstack/eslint-plugin-security
|
73 | "@rushstack/eslint-plugin-security",
|
74 | // Plugin documentation: https://www.npmjs.com/package/@typescript-eslint/eslint-plugin
|
75 | "@typescript-eslint/eslint-plugin",
|
76 | // Plugin documentation: https://www.npmjs.com/package/eslint-plugin-jsdoc
|
77 | "eslint-plugin-jsdoc",
|
78 | // Plugin documentation: https://www.npmjs.com/package/eslint-plugin-promise
|
79 | "eslint-plugin-promise",
|
80 | // Plugin documentation: https://www.npmjs.com/package/eslint-plugin-tsdoc
|
81 | "eslint-plugin-tsdoc",
|
82 | // Plugin documentation: https://www.npmjs.com/package/eslint-plugin-unused-imports
|
83 | "unused-imports",
|
84 | // Plugin documentation: https://www.npmjs.com/package/eslint-plugin-unicorn
|
85 | "unicorn",
|
86 | // Custom ESLint rules
|
87 | "@fluid-internal/eslint-plugin-fluid",
|
88 | ],
|
89 | reportUnusedDisableDirectives: true,
|
90 | ignorePatterns: [
|
91 | // Don't lint generated packageVersion files.
|
92 | "**/packageVersion.ts",
|
93 | ],
|
94 | rules: {
|
95 | /**
|
96 | * Restricts including release tags inside the member class / interface.
|
97 | *
|
98 | * Refer to the rule by the unprefixed plugin name in the consumed package.
|
99 | * {@link https://eslint.org/docs/latest/extend/plugins#rules-in-plugins}
|
100 | */
|
101 | "@fluid-internal/fluid/no-member-release-tags": "error",
|
102 |
|
103 | /**
|
104 | * The @rushstack rules are documented in the package README:
|
105 | * {@link https://www.npmjs.com/package/@rushstack/eslint-plugin}
|
106 | */
|
107 | "@rushstack/no-new-null": "warn",
|
108 |
|
109 | /**
|
110 | * RATIONALE: Harmless.
|
111 | *
|
112 | * Our guideline is to only use leading underscores on private members when required to avoid a conflict
|
113 | * between private fields and a public property.
|
114 | *
|
115 | * Docs: {@link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/naming-convention.md}
|
116 | */
|
117 | "@typescript-eslint/naming-convention": [
|
118 | "error",
|
119 | {
|
120 | selector: "accessor",
|
121 | modifiers: ["private"],
|
122 | format: ["camelCase"],
|
123 | leadingUnderscore: "allow",
|
124 | },
|
125 | ],
|
126 |
|
127 | /**
|
128 | * Encourages minimal disabling of eslint rules, while still permitting whole-file exclusions.
|
129 | */
|
130 | "eslint-comments/disable-enable-pair": [
|
131 | "error",
|
132 | {
|
133 | allowWholeFile: true,
|
134 | },
|
135 | ],
|
136 |
|
137 | // ENABLED INTENTIONALLY
|
138 | "@typescript-eslint/ban-types": "error",
|
139 | "@typescript-eslint/dot-notation": "error",
|
140 | "@typescript-eslint/no-non-null-assertion": "error",
|
141 | "@typescript-eslint/no-unnecessary-type-assertion": "error",
|
142 |
|
143 | "eqeqeq": ["error", "smart"],
|
144 | "import/no-deprecated": "error",
|
145 | "max-len": [
|
146 | "error",
|
147 | {
|
148 | code: 120,
|
149 | ignoreTrailingComments: true,
|
150 | ignoreUrls: true,
|
151 | ignoreStrings: true,
|
152 | ignoreTemplateLiterals: true,
|
153 | ignoreRegExpLiterals: true,
|
154 | },
|
155 | ],
|
156 | "no-multi-spaces": [
|
157 | "error",
|
158 | {
|
159 | ignoreEOLComments: true,
|
160 | },
|
161 | ],
|
162 |
|
163 | /**
|
164 | * Note: this can be replaced altogether by `@typescript-eslint/no-unused-vars`,
|
165 | * but that rule covers many more scenarios than this one does, and there are many violations
|
166 | * currently in the repository, so it has not been enabled yet.
|
167 | */
|
168 | "unused-imports/no-unused-imports": "error",
|
169 |
|
170 | "valid-typeof": "error",
|
171 |
|
172 | /**
|
173 | * Catches a common coding mistake where "resolve" and "reject" are confused.
|
174 | */
|
175 | "promise/param-names": "warn",
|
176 |
|
177 | "unicorn/better-regex": "error",
|
178 | "unicorn/filename-case": [
|
179 | "error",
|
180 | {
|
181 | cases: {
|
182 | camelCase: true,
|
183 | pascalCase: true,
|
184 | },
|
185 | },
|
186 | ],
|
187 | "unicorn/no-new-buffer": "error",
|
188 | "unicorn/prefer-switch": "error",
|
189 | "unicorn/prefer-ternary": "error",
|
190 | "unicorn/prefer-type-error": "error",
|
191 |
|
192 | // #region DISABLED INTENTIONALLY
|
193 |
|
194 | /**
|
195 | * Disabled because we don't require that all variable declarations be explicitly typed.
|
196 | */
|
197 | "@rushstack/typedef-var": "off",
|
198 | "@typescript-eslint/explicit-function-return-type": "off",
|
199 | "@typescript-eslint/explicit-member-accessibility": "off",
|
200 |
|
201 | /**
|
202 | * Disabled because we will lean on the formatter (i.e. prettier) to enforce indentation policy.
|
203 | */
|
204 | "@typescript-eslint/indent": "off",
|
205 | "@typescript-eslint/member-ordering": "off",
|
206 | "@typescript-eslint/no-explicit-any": "off",
|
207 | "@typescript-eslint/no-unused-vars": "off",
|
208 | "@typescript-eslint/no-use-before-define": "off",
|
209 | "@typescript-eslint/typedef": "off",
|
210 |
|
211 | /**
|
212 | * Disabled because we want to encourage documenting different events separately.
|
213 | */
|
214 | "@typescript-eslint/unified-signatures": "off",
|
215 |
|
216 | // Requires a lot of changes
|
217 | "@typescript-eslint/no-duplicate-type-constituents": "off",
|
218 |
|
219 | // Lots of false positives
|
220 | "@typescript-eslint/non-nullable-type-assertion-style": "off",
|
221 |
|
222 | // Requires breaking changes; enabled in the strict config
|
223 | "@typescript-eslint/consistent-indexed-object-style": "off",
|
224 |
|
225 | // Requires a lot of changes; enabled in the strict config
|
226 | "@typescript-eslint/no-unsafe-enum-comparison": "off",
|
227 |
|
228 | // Requires a lot of changes; enabled in the strict config
|
229 | "@typescript-eslint/no-redundant-type-constituents": "off",
|
230 |
|
231 | // Requires a lot of changes; enabled in the strict config
|
232 | "@typescript-eslint/consistent-generic-constructors": "off",
|
233 |
|
234 | // Off for minimal and recommended; enabled in the strict config
|
235 | "@typescript-eslint/consistent-type-exports": "off",
|
236 | "@typescript-eslint/consistent-type-imports": "off",
|
237 |
|
238 | "func-call-spacing": "off", // Off because it conflicts with typescript-formatter
|
239 | "no-empty": "off",
|
240 | "no-void": "off",
|
241 | "require-atomic-updates": "off",
|
242 |
|
243 | /**
|
244 | * Superseded by `@typescript-eslint/dot-notation`.
|
245 | */
|
246 | "dot-notation": "off",
|
247 |
|
248 | /**
|
249 | * Superseded by `@typescript-eslint/no-unused-expressions`.
|
250 | */
|
251 | "no-unused-expressions": "off",
|
252 |
|
253 | // #endregion
|
254 |
|
255 | // #region FORMATTING RULES
|
256 |
|
257 | // Disabled because it conflicts with formatter rules
|
258 | "@typescript-eslint/brace-style": "off",
|
259 | "@typescript-eslint/comma-spacing": "error",
|
260 | "@typescript-eslint/func-call-spacing": "error",
|
261 | "@typescript-eslint/keyword-spacing": "error",
|
262 | "@typescript-eslint/member-delimiter-style": [
|
263 | "error",
|
264 | {
|
265 | multiline: {
|
266 | delimiter: "semi",
|
267 | requireLast: true,
|
268 | },
|
269 | singleline: {
|
270 | delimiter: "semi",
|
271 | requireLast: true,
|
272 | },
|
273 | multilineDetection: "brackets",
|
274 | },
|
275 | ],
|
276 | "@typescript-eslint/object-curly-spacing": ["error", "always"],
|
277 | "@typescript-eslint/semi": ["error", "always"],
|
278 | "@typescript-eslint/space-before-function-paren": [
|
279 | "error",
|
280 | {
|
281 | anonymous: "never",
|
282 | asyncArrow: "always",
|
283 | named: "never",
|
284 | },
|
285 | ],
|
286 | "@typescript-eslint/space-infix-ops": "error",
|
287 | "@typescript-eslint/type-annotation-spacing": "error",
|
288 | "array-bracket-spacing": "error",
|
289 | "arrow-spacing": "error",
|
290 | "block-spacing": "error",
|
291 | "dot-location": ["error", "property"],
|
292 | "jsx-quotes": "error",
|
293 | "key-spacing": "error",
|
294 | "space-unary-ops": "error",
|
295 | "switch-colon-spacing": "error",
|
296 |
|
297 | // #endregion
|
298 |
|
299 | // #region DOCUMENTATION RULES
|
300 |
|
301 | /**
|
302 | * This rule ensures that our Intellisense looks good by verifying the TSDoc syntax.
|
303 | */
|
304 | "tsdoc/syntax": "error",
|
305 |
|
306 | // #region eslint-plugin-jsdoc rules
|
307 |
|
308 | /**
|
309 | * Ensures that conflicting access tags don't exist in the same comment.
|
310 | * See <https://github.com/gajus/eslint-plugin-jsdoc#check-access>.
|
311 | */
|
312 | "jsdoc/check-access": "error",
|
313 |
|
314 | /**
|
315 | * Ensures consistent line formatting in JSDoc/TSDoc comments
|
316 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-check-alignment>
|
317 | *
|
318 | * TODO: This is temporarily set to "warn" because there are a lot of false positives with code blocks in
|
319 | * particular.
|
320 | */
|
321 | "jsdoc/check-line-alignment": "warn",
|
322 |
|
323 | /**
|
324 | * The syntax this validates does not accommodate the syntax used by API-Extractor
|
325 | * See <https://api-extractor.com/pages/tsdoc/tag_example/>
|
326 | */
|
327 | "jsdoc/check-examples": "off",
|
328 |
|
329 | /**
|
330 | * Ensures correct indentation within JSDoc/TSDoc comment body
|
331 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-check-indentation>
|
332 | */
|
333 | "jsdoc/check-indentation": "error",
|
334 |
|
335 | /**
|
336 | * Covered by `tsdoc/syntax`
|
337 | */
|
338 | "jsdoc/check-tag-names": "off",
|
339 |
|
340 | /**
|
341 | * Ensures that JSDoc/TSDoc "modifier" tags are empty.
|
342 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-empty-tags>
|
343 | */
|
344 | "jsdoc/empty-tags": "error",
|
345 |
|
346 | /**
|
347 | * Ensures multi-line formatting meets JSDoc/TSDoc requirements.
|
348 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-no-bad-blocks>
|
349 | */
|
350 | "jsdoc/no-bad-blocks": "error",
|
351 |
|
352 | /**
|
353 | * Requires that each line in a JSDoc/TSDoc comment starts with a `*`.
|
354 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-require-asterisk-prefix>
|
355 | */
|
356 | "jsdoc/require-asterisk-prefix": "error",
|
357 |
|
358 | /**
|
359 | * Ensure function/method parameter comments include a `-` between name and description.
|
360 | * Useful to ensure API-Extractor compatability.
|
361 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-require-hyphen-before-param-description>.
|
362 | */
|
363 | "jsdoc/require-hyphen-before-param-description": "error",
|
364 |
|
365 | /**
|
366 | * Require `@param` tags be non-empty.
|
367 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-require-param-description>
|
368 | */
|
369 | "jsdoc/require-param-description": "error",
|
370 |
|
371 | /**
|
372 | * Requires `@returns` tags to be non-empty.
|
373 | * See <https://github.com/gajus/eslint-plugin-jsdoc#user-content-eslint-plugin-jsdoc-rules-require-returns-description>
|
374 | */
|
375 | "jsdoc/require-returns-description": "error",
|
376 |
|
377 | // #endregion
|
378 |
|
379 | // #endregion
|
380 |
|
381 | "@typescript-eslint/prefer-includes": "error",
|
382 | "@typescript-eslint/prefer-nullish-coalescing": "error",
|
383 | "@typescript-eslint/prefer-optional-chain": "error",
|
384 |
|
385 | /**
|
386 | * By default, libraries should not take dependencies on node libraries.
|
387 | * This rule can be disabled at the project level for libraries that are intended to be used only in node.
|
388 | */
|
389 | "import/no-nodejs-modules": ["error"],
|
390 |
|
391 | /**
|
392 | * Allow Fluid Framework to import from its own internal packages.
|
393 | * https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-internal-modules.md
|
394 | */
|
395 | "import/no-internal-modules": [
|
396 | "error",
|
397 | {
|
398 | allow: permittedImports,
|
399 | },
|
400 | ],
|
401 | },
|
402 | overrides: [
|
403 | {
|
404 | // Rules only for TypeScript files
|
405 | files: ["*.ts", "*.tsx"],
|
406 | rules: {
|
407 | "dot-notation": "off", // Superseded by @typescript-eslint/dot-notation
|
408 | "no-unused-expressions": "off", // Superseded by @typescript-eslint/no-unused-expressions
|
409 | },
|
410 | settings: {
|
411 | jsdoc: {
|
412 | mode: "typescript",
|
413 | },
|
414 | },
|
415 | },
|
416 | {
|
417 | // Rules only for React files
|
418 | files: ["*.jsx", "*.tsx"],
|
419 | plugins: [
|
420 | // Plugin documentation: https://www.npmjs.com/package/eslint-plugin-react
|
421 | "react",
|
422 |
|
423 | // Plugin documentation: https://www.npmjs.com/package/eslint-plugin-react-hooks
|
424 | "react-hooks",
|
425 | ],
|
426 | extends: ["plugin:react/recommended", "plugin:react-hooks/recommended"],
|
427 | settings: {
|
428 | react: {
|
429 | version: "detect",
|
430 | },
|
431 | },
|
432 | },
|
433 | {
|
434 | // Rules only for test files
|
435 | files: ["*.spec.ts", "*.test.ts", "**/test/**"],
|
436 | rules: {
|
437 | "@typescript-eslint/no-invalid-this": "off",
|
438 | "@typescript-eslint/unbound-method": "off", // This rule has false positives in many of our test projects.
|
439 | "import/no-nodejs-modules": "off", // Node libraries are OK for test files.
|
440 | "import/no-deprecated": "off", // Deprecated APIs are OK to use in test files.
|
441 |
|
442 | // Disabled for test files
|
443 | "@typescript-eslint/consistent-type-exports": "off",
|
444 | "@typescript-eslint/consistent-type-imports": "off",
|
445 |
|
446 | // For test files only, additionally allow import of '/test*' and '/internal/test*' exports.
|
447 | "import/no-internal-modules": [
|
448 | "error",
|
449 | {
|
450 | allow: ["@fluid*/*/test*", "@fluid*/*/internal/test*"].concat(
|
451 | permittedImports,
|
452 | ),
|
453 | },
|
454 | ],
|
455 | },
|
456 | },
|
457 | ],
|
458 | settings: {
|
459 | "import/extensions": [".ts", ".tsx", ".d.ts", ".js", ".jsx"],
|
460 | "import/parsers": {
|
461 | "@typescript-eslint/parser": [".ts", ".tsx", ".d.ts"],
|
462 | },
|
463 | "import/resolver": {
|
464 | /**
|
465 | * Note: the key order of import/resolver is relevant in the completely resolved eslint config (see ./printed-configs).
|
466 | * Resolvers are tried in key order, and the first one to successfully resolve the import wins. See:
|
467 | * https://github.com/import-js/eslint-plugin-import/blob/c0ac54b8a721c2b1c9048838acc4d6282f4fe7a7/utils/resolve.js#L196
|
468 | *
|
469 | * It's important that the typescript resolver is first, as the node resolver legitimately resolves some imports to modules
|
470 | * with stripped type information, which can cause silent negatives in lint rules. For example, import/no-deprecated fails
|
471 | * to lint against import and usage of deprecated types when the import is resolvable and resolved using the node resolver.
|
472 | */
|
473 | typescript: {
|
474 | extensions: [".ts", ".tsx", ".d.ts", ".js", ".jsx"],
|
475 | conditionNames: [
|
476 | // This supports the test-only conditional export pattern used in merge-tree and id-compressor.
|
477 | "allow-ff-test-exports",
|
478 |
|
479 | // Default condition names below, see https://www.npmjs.com/package/eslint-import-resolver-typescript#conditionnames
|
480 | "types",
|
481 | "import",
|
482 |
|
483 | // APF: https://angular.io/guide/angular-package-format
|
484 | "esm2020",
|
485 | "es2020",
|
486 | "es2015",
|
487 |
|
488 | "require",
|
489 | "node",
|
490 | "node-addons",
|
491 | "browser",
|
492 | "default",
|
493 | ],
|
494 | },
|
495 | },
|
496 | "jsdoc": {
|
497 | // The following are intended to keep js/jsx JSDoc comments in line with TSDoc syntax used in ts/tsx code.
|
498 | tagNamePreference: {
|
499 | arg: {
|
500 | message: "Please use @param instead of @arg.",
|
501 | replacement: "param",
|
502 | },
|
503 | argument: {
|
504 | message: "Please use @param instead of @argument.",
|
505 | replacement: "param",
|
506 | },
|
507 | return: {
|
508 | message: "Please use @returns instead of @return.",
|
509 | replacement: "returns",
|
510 | },
|
511 | },
|
512 | },
|
513 | },
|
514 | };
|