0
# @remix-run/eslint-config
1
2
@remix-run/eslint-config provides comprehensive ESLint configurations specifically designed for Remix projects. It offers shareable ESLint configs that enforce best practices for React, TypeScript, JSX accessibility, and import/export patterns.
3
4
**⚠️ Deprecation Notice**: This package is deprecated and will not be included in React Router v7. Users should migrate to streamlined ESLint configurations as provided in Remix templates.
5
6
## Package Information
7
8
- **Package Name**: @remix-run/eslint-config
9
- **Package Type**: npm
10
- **Language**: JavaScript/TypeScript
11
- **Installation**: `npm install -D eslint @remix-run/eslint-config`
12
13
## Core Imports
14
15
This package is used via ESLint configuration files - no JavaScript imports required.
16
17
## Basic Usage
18
19
Create a `.eslintrc.js` file in your project root:
20
21
```javascript
22
module.exports = {
23
extends: "@remix-run/eslint-config",
24
};
25
```
26
27
With Jest and Testing Library:
28
29
```javascript
30
module.exports = {
31
extends: [
32
"@remix-run/eslint-config",
33
"@remix-run/eslint-config/jest-testing-library",
34
],
35
};
36
```
37
38
For Node.js projects:
39
40
```javascript
41
module.exports = {
42
extends: [
43
"@remix-run/eslint-config",
44
"@remix-run/eslint-config/node",
45
],
46
};
47
```
48
49
**Note**: The package.json references a `jest.js` file in the files array, but this file does not exist in the actual package. Testing configuration is provided through `jest-testing-library.js` instead.
50
51
## Architecture
52
53
@remix-run/eslint-config is built around several key configuration modules:
54
55
- **Main Configuration**: Default shareable config with React, TypeScript, and accessibility rules
56
- **Testing Configuration**: Optional Jest and Testing Library rules for test files
57
- **Node.js Configuration**: Basic Node.js environment setup
58
- **Internal Configuration**: Extended configuration for internal Remix development (not recommended for external use)
59
- **Rule Modules**: Modular rule definitions for core JavaScript, React, TypeScript, accessibility, and testing
60
- **Settings Modules**: Import resolution and React-specific settings for Remix components
61
62
## Capabilities
63
64
### Default ESLint Configuration
65
66
Main shareable ESLint configuration for Remix projects with comprehensive rules for JavaScript, React, TypeScript, and accessibility.
67
68
```javascript { .api }
69
// Usage in .eslintrc.js
70
module.exports = {
71
extends: "@remix-run/eslint-config",
72
};
73
74
// ESLint Configuration Object Structure
75
interface ESLintConfig {
76
parser: string;
77
parserOptions: {
78
sourceType: "module";
79
requireConfigFile: false;
80
ecmaVersion: "latest";
81
babelOptions: {
82
presets: string[];
83
};
84
};
85
env: {
86
browser: boolean;
87
commonjs: boolean;
88
es6: boolean;
89
};
90
plugins: string[];
91
settings: Record<string, any>;
92
rules: Record<string, any>;
93
overrides: Array<{
94
files: string[];
95
extends?: string[];
96
parser?: string;
97
parserOptions?: Record<string, any>;
98
plugins?: string[];
99
rules?: Record<string, any>;
100
}>;
101
}
102
```
103
104
**Features:**
105
- **JavaScript/TypeScript Parsing**: Uses @babel/eslint-parser and @typescript-eslint/parser
106
- **React Support**: React and React Hooks rules with JSX support
107
- **Accessibility**: JSX A11y rules for accessible React components
108
- **Import Management**: ES6 import/export rules with TypeScript integration
109
- **TypeScript Overrides**: Specialized rules for .ts/.tsx files
110
- **Route Overrides**: Special handling for Remix route files (allows unnamed default exports)
111
112
### Jest and Testing Library Configuration
113
114
Optional ESLint configuration for projects using Jest and Testing Library.
115
116
```javascript { .api }
117
// Usage in .eslintrc.js
118
module.exports = {
119
extends: [
120
"@remix-run/eslint-config",
121
"@remix-run/eslint-config/jest-testing-library",
122
],
123
};
124
125
// Configuration Structure
126
interface JestTestingLibraryConfig {
127
plugins: ["jest", "jest-dom", "testing-library"];
128
env: {
129
node: boolean;
130
};
131
overrides: Array<{
132
files: string[];
133
env: {
134
"jest/globals": boolean;
135
};
136
rules: Record<string, any>;
137
}>;
138
}
139
```
140
141
**Features:**
142
- **Jest Rules**: Test structure, expect usage, snapshot testing guidelines
143
- **Jest DOM Rules**: DOM assertion preferences and element state checking
144
- **Testing Library Rules**: Best practices for async queries, event handling, screen queries
145
- **Test File Targeting**: Applies rules to `**/__tests__/**/*` and `**/*.{spec,test}.*` files
146
147
**Required Peer Dependencies** (if using this configuration):
148
```json
149
{
150
"dependencies": {
151
"@testing-library/jest-dom": ">=5.16.0",
152
"@testing-library/react": ">=12.0.0",
153
"jest": ">=28.0.0"
154
}
155
}
156
```
157
158
### Node.js Configuration
159
160
Basic Node.js environment configuration for server-side projects.
161
162
```javascript { .api }
163
// Usage in .eslintrc.js
164
module.exports = {
165
extends: [
166
"@remix-run/eslint-config",
167
"@remix-run/eslint-config/node",
168
],
169
};
170
171
// Configuration Structure
172
interface NodeConfig {
173
plugins: ["node"];
174
env: {
175
node: boolean;
176
};
177
}
178
```
179
180
**Features:**
181
- **Node.js Environment**: Enables Node.js global variables and features
182
- **Node.js Plugin**: Provides Node.js-specific linting rules
183
184
### Internal Configuration (Not Public API)
185
186
Extended ESLint configuration used internally by the Remix team. This configuration is not intended for external use and should not be considered public API regarding semver considerations.
187
188
```javascript { .api }
189
// Usage (internal Remix projects only)
190
module.exports = {
191
extends: "@remix-run/eslint-config/internal",
192
};
193
194
// Configuration Structure
195
interface InternalConfig {
196
root: true;
197
extends: [
198
"./index.js",
199
"./jest-testing-library.js"
200
];
201
env: {
202
node: boolean;
203
};
204
plugins: ["node", "prefer-let"];
205
rules: {
206
"@typescript-eslint/consistent-type-imports": 2; // ERROR
207
"import/order": [2, {
208
"newlines-between": "always";
209
groups: [
210
["builtin", "external"],
211
"internal",
212
["parent", "sibling", "index"]
213
];
214
}];
215
"jest/no-disabled-tests": 0; // OFF
216
"prefer-let/prefer-let": 1; // WARN
217
};
218
overrides: Array<{
219
files: string[];
220
rules: Record<string, any>;
221
}>;
222
}
223
```
224
225
**Features:**
226
- **Extended Base Config**: Includes both main config and jest-testing-library config
227
- **Import Ordering**: Enforces specific import organization with newlines between groups
228
- **TypeScript Imports**: Requires consistent type-only imports
229
- **Markdown Code Blocks**: Special rules for code samples in documentation
230
- **Workspace Dependencies**: Validates dependencies are listed in package.json files
231
- **Integration Test Handling**: Special rules for integration test files
232
233
**Internal-Only Rules:**
234
- Consistent type imports enforcement
235
- Strict import ordering with grouped organization
236
- Prefer-let over const for variable declarations
237
- Disabled Jest rules for certain test patterns
238
- Code block linting in Markdown files with relaxed rules
239
240
## Rule Categories
241
242
### Core JavaScript Rules
243
244
Fundamental ESLint rules for JavaScript/ES6+ code quality and error prevention.
245
246
```javascript { .api }
247
// Core JavaScript Rules (comprehensive)
248
interface CoreRules {
249
"array-callback-return": 1; // WARN
250
"getter-return": 1; // WARN
251
"new-parens": 1; // WARN
252
"no-array-constructor": 1; // WARN
253
"no-caller": 2; // ERROR
254
"no-cond-assign": [1, "except-parens"]; // WARN
255
"no-const-assign": 2; // ERROR
256
"no-control-regex": 1; // WARN
257
"no-dupe-args": 1; // WARN
258
"no-dupe-class-members": 1; // WARN
259
"no-dupe-keys": 1; // WARN
260
"no-duplicate-case": 1; // WARN
261
"no-empty-character-class": 1; // WARN
262
"no-empty-pattern": 1; // WARN
263
"no-empty": [1, { allowEmptyCatch: true }]; // WARN
264
"no-eval": 2; // ERROR
265
"no-ex-assign": 1; // WARN
266
"no-extend-native": 1; // WARN
267
"no-extra-bind": 1; // WARN
268
"no-extra-label": 1; // WARN
269
"no-extra-boolean-cast": 1; // WARN
270
"no-func-assign": 2; // ERROR
271
"no-global-assign": 2; // ERROR
272
"no-implied-eval": 1; // WARN
273
"no-invalid-regexp": 1; // WARN
274
"no-label-var": 1; // WARN
275
"no-labels": [1, { allowLoop: true, allowSwitch: false }]; // WARN
276
"no-lone-blocks": 1; // WARN
277
"no-loop-func": 1; // WARN
278
"no-mixed-operators": [1, {
279
groups: [
280
["&", "|", "^", "~", "<<", ">>", ">>>"],
281
["==", "!=", "===", "!==", ">", ">=", "<", "<="],
282
["&&", "||"],
283
["in", "instanceof"]
284
],
285
allowSamePrecedence: false
286
}]; // WARN
287
"no-unsafe-negation": 1; // WARN
288
"no-new-func": 1; // WARN
289
"no-new-object": 1; // WARN
290
"no-octal": 1; // WARN
291
"no-redeclare": 2; // ERROR
292
"no-script-url": 1; // WARN
293
"no-self-assign": 1; // WARN
294
"no-self-compare": 1; // WARN
295
"no-sequences": 1; // WARN
296
"no-shadow-restricted-names": 1; // WARN
297
"no-sparse-arrays": 1; // WARN
298
"no-template-curly-in-string": 1; // WARN
299
"no-this-before-super": 1; // WARN
300
"no-undef": 2; // ERROR
301
"no-unreachable": 1; // WARN
302
"no-unused-expressions": [1, {
303
allowShortCircuit: true,
304
allowTernary: true,
305
allowTaggedTemplates: true
306
}]; // WARN
307
"no-unused-labels": 1; // WARN
308
"no-unused-vars": [1, { args: "none", ignoreRestSiblings: true }]; // WARN
309
"no-use-before-define": [1, { classes: false, functions: false, variables: false }]; // WARN
310
"no-useless-computed-key": 1; // WARN
311
"no-useless-concat": 1; // WARN
312
"no-useless-constructor": 1; // WARN
313
"no-useless-escape": 1; // WARN
314
"no-useless-rename": [1, {
315
ignoreDestructuring: false,
316
ignoreImport: false,
317
ignoreExport: false
318
}]; // WARN
319
"require-yield": 1; // WARN
320
"use-isnan": 1; // WARN
321
"valid-typeof": 1; // WARN
322
}
323
```
324
325
### React and JSX Rules
326
327
React-specific rules for component development and JSX syntax.
328
329
```javascript { .api }
330
// React and JSX Rules (comprehensive)
331
interface ReactRules {
332
"react/display-name": 1; // WARN
333
"react/forbid-foreign-prop-types": [1, { allowInPropTypes: true }]; // WARN
334
"react/jsx-key": 1; // WARN
335
"react/jsx-no-comment-textnodes": 1; // WARN
336
"react/jsx-no-target-blank": 1; // WARN
337
"react/jsx-no-undef": 2; // ERROR
338
"react/jsx-pascal-case": [1, { allowAllCaps: true, ignore: [] }]; // WARN
339
"react/jsx-uses-vars": 1; // WARN
340
"react/jsx-uses-react": 1; // WARN
341
"react/no-danger-with-children": 1; // WARN
342
"react/no-direct-mutation-state": 1; // WARN
343
"react/no-find-dom-node": 1; // WARN
344
"react/no-is-mounted": 1; // WARN
345
"react/no-render-return-value": 2; // ERROR
346
"react/no-string-refs": 1; // WARN
347
"react/no-typos": 1; // WARN
348
"react/react-in-jsx-scope": 0; // OFF - Not needed in modern React
349
"react/require-render-return": 2; // ERROR
350
"react/style-prop-object": 1; // WARN
351
352
// React Hooks Rules
353
"react-hooks/exhaustive-deps": 1; // WARN
354
"react-hooks/rules-of-hooks": 2; // ERROR
355
}
356
```
357
358
### TypeScript Rules
359
360
TypeScript-specific rules and overrides for type safety.
361
362
```javascript { .api }
363
// TypeScript Rules (comprehensive)
364
interface TypeScriptRules {
365
// Disabled TypeScript rules (turned off for flexibility)
366
"@typescript-eslint/ban-ts-comment": 0; // OFF
367
"@typescript-eslint/ban-types": 0; // OFF
368
"@typescript-eslint/no-empty-function": 0; // OFF
369
"@typescript-eslint/no-empty-interface": 0; // OFF
370
"@typescript-eslint/no-explicit-any": 0; // OFF
371
"@typescript-eslint/no-inferrable-types": 0; // OFF
372
"@typescript-eslint/no-namespace": 0; // OFF
373
"@typescript-eslint/no-non-null-assertion": 0; // OFF
374
"@typescript-eslint/no-var-requires": 0; // OFF
375
"no-var": 0; // OFF
376
"prefer-rest-params": 0; // OFF
377
378
// Configured TypeScript rules
379
"@typescript-eslint/no-use-before-define": [2, {
380
functions: false,
381
classes: false,
382
variables: false,
383
typedefs: false
384
}]; // ERROR
385
"@typescript-eslint/no-unused-expressions": [2, {
386
allowShortCircuit: true,
387
allowTernary: true,
388
allowTaggedTemplates: true
389
}]; // ERROR
390
"@typescript-eslint/no-unused-vars": [2, {
391
args: "none",
392
ignoreRestSiblings: true
393
}]; // ERROR
394
395
// JavaScript rules disabled for TypeScript
396
"no-dupe-class-members": 0; // OFF - Handled by TypeScript
397
"no-undef": 0; // OFF - Handled by TypeScript
398
"no-use-before-define": 0; // OFF - Use TypeScript version
399
"prefer-const": 0; // OFF - Style preference
400
401
// Stylistic TypeScript rules
402
"@typescript-eslint/consistent-type-assertions": 1; // WARN
403
"@typescript-eslint/consistent-type-imports": 1; // WARN
404
}
405
```
406
407
### JSX Accessibility Rules
408
409
Accessibility rules for JSX elements and ARIA compliance.
410
411
```javascript { .api }
412
// JSX Accessibility Rules (comprehensive)
413
interface JsxA11yRules {
414
"jsx-a11y/alt-text": 1; // WARN
415
"jsx-a11y/anchor-has-content": [1, { components: ["Link", "NavLink"] }]; // WARN
416
"jsx-a11y/anchor-is-valid": [1, { aspects: ["noHref", "invalidHref"] }]; // WARN
417
"jsx-a11y/aria-activedescendant-has-tabindex": 1; // WARN
418
"jsx-a11y/aria-props": 1; // WARN
419
"jsx-a11y/aria-proptypes": 1; // WARN
420
"jsx-a11y/aria-role": [1, { ignoreNonDOM: true }]; // WARN
421
"jsx-a11y/aria-unsupported-elements": 1; // WARN
422
"jsx-a11y/iframe-has-title": 1; // WARN
423
"jsx-a11y/img-redundant-alt": 1; // WARN
424
"jsx-a11y/lang": 1; // WARN
425
"jsx-a11y/no-access-key": 1; // WARN
426
"jsx-a11y/no-redundant-roles": 1; // WARN
427
"jsx-a11y/role-has-required-aria-props": 1; // WARN
428
"jsx-a11y/role-supports-aria-props": 1; // WARN
429
}
430
```
431
432
### Import and Export Rules
433
434
ES6 module import/export management and organization.
435
436
```javascript { .api }
437
// Import and Export Rules
438
interface ImportRules {
439
"import/first": 2; // ERROR - Imports must be at top
440
"import/no-amd": 2; // ERROR - No AMD require/define
441
"import/no-duplicates": 2; // ERROR - No duplicate imports
442
"import/no-webpack-loader-syntax": 2; // ERROR - No webpack loader syntax
443
}
444
```
445
446
### Jest Testing Rules
447
448
Jest-specific rules for test structure and best practices when using jest-testing-library configuration.
449
450
```javascript { .api }
451
// Jest Rules (comprehensive)
452
interface JestRules {
453
"jest/no-conditional-expect": 1; // WARN
454
"jest/no-deprecated-functions": 1; // WARN
455
"jest/no-disabled-tests": 1; // WARN
456
"jest/no-export": 2; // ERROR - Test files should not export
457
"jest/no-focused-tests": 1; // WARN - No .only or .skip
458
"jest/no-identical-title": 1; // WARN
459
"jest/no-interpolation-in-snapshots": 1; // WARN
460
"jest/no-jasmine-globals": 2; // ERROR - Use Jest, not Jasmine
461
"jest/no-jest-import": 1; // WARN
462
"jest/no-mocks-import": 1; // WARN
463
"jest/valid-describe-callback": 2; // ERROR
464
"jest/valid-expect": 2; // ERROR
465
"jest/valid-expect-in-promise": 2; // ERROR
466
}
467
```
468
469
### Jest DOM Rules
470
471
Jest DOM assertion preferences and element state checking.
472
473
```javascript { .api }
474
// Jest DOM Rules
475
interface JestDomRules {
476
"jest-dom/prefer-checked": 1; // WARN
477
"jest-dom/prefer-empty": 1; // WARN
478
"jest-dom/prefer-enabled-disabled": 1; // WARN
479
"jest-dom/prefer-focus": 1; // WARN
480
"jest-dom/prefer-in-document": 1; // WARN
481
"jest-dom/prefer-required": 1; // WARN
482
"jest-dom/prefer-to-have-attribute": 1; // WARN
483
"jest-dom/prefer-to-have-class": 1; // WARN
484
"jest-dom/prefer-to-have-style": 1; // WARN
485
"jest-dom/prefer-to-have-text-content": 1; // WARN
486
"jest-dom/prefer-to-have-value": 1; // WARN
487
}
488
```
489
490
### Testing Library Rules
491
492
Testing Library best practices for async queries, event handling, and screen queries.
493
494
```javascript { .api }
495
// Testing Library Rules
496
interface TestingLibraryRules {
497
"testing-library/await-async-query": 2; // ERROR
498
"testing-library/await-async-utils": 2; // ERROR
499
"testing-library/no-await-sync-events": 2; // ERROR
500
"testing-library/no-await-sync-query": 2; // ERROR
501
"testing-library/no-debugging-utils": 1; // WARN
502
"testing-library/no-promise-in-fire-event": 2; // ERROR
503
"testing-library/no-render-in-setup": 2; // ERROR
504
"testing-library/no-unnecessary-act": 2; // ERROR
505
"testing-library/no-wait-for-empty-callback": 2; // ERROR
506
"testing-library/no-wait-for-multiple-assertions": 2; // ERROR
507
"testing-library/no-wait-for-side-effects": 2; // ERROR
508
"testing-library/no-wait-for-snapshot": 2; // ERROR
509
"testing-library/prefer-find-by": 1; // WARN
510
"testing-library/prefer-presence-queries": 1; // WARN
511
"testing-library/prefer-query-by-disappearance": 1; // WARN
512
"testing-library/prefer-screen-queries": 1; // WARN
513
"testing-library/prefer-user-event": 1; // WARN
514
"testing-library/prefer-wait-for": 1; // WARN
515
"testing-library/render-result-naming-convention": 1; // WARN
516
}
517
```
518
519
## Configuration Settings
520
521
### React Settings
522
523
React-specific ESLint settings for Remix components.
524
525
```javascript { .api }
526
interface ReactSettings {
527
react: {
528
version: "detect";
529
formComponents: ["Form"];
530
linkComponents: Array<{
531
name: "Link" | "NavLink";
532
linkAttribute: "to";
533
}>;
534
};
535
}
536
```
537
538
### Import Resolution Settings
539
540
Import parser and resolver configuration for JavaScript and TypeScript.
541
542
```javascript { .api }
543
// Import Resolution Settings (detailed)
544
interface ImportSettings {
545
"import/ignore": [
546
"node_modules",
547
"\\.(css|md|svg|json)$"
548
];
549
"import/parsers": {
550
"@typescript-eslint/parser": [".ts", ".tsx", ".d.ts"];
551
};
552
"import/resolver": {
553
"eslint-import-resolver-node": {
554
extensions: [".js", ".jsx", ".ts", ".tsx"];
555
};
556
"eslint-import-resolver-typescript": {
557
alwaysTryTypes: true;
558
};
559
};
560
}
561
```
562
563
## File Override Patterns
564
565
### TypeScript File Overrides
566
567
Special configuration for `.ts` and `.tsx` files.
568
569
```javascript { .api }
570
// Files: ["**/*.ts?(x)"]
571
interface TypeScriptOverride {
572
extends: [
573
"plugin:import/typescript",
574
"plugin:@typescript-eslint/recommended",
575
];
576
parser: "@typescript-eslint/parser";
577
parserOptions: {
578
sourceType: "module";
579
ecmaVersion: 2019;
580
};
581
plugins: ["@typescript-eslint"];
582
rules: Record<string, any>;
583
}
584
```
585
586
### Remix Route File Overrides
587
588
Special handling for Remix route files allowing unnamed default exports.
589
590
```javascript { .api }
591
// Files: ["**/routes/**/*.js?(x)", "**/routes/**/*.tsx", "app/root.js?(x)", "app/root.tsx"]
592
interface RouteOverride {
593
rules: {
594
"react/display-name": 0; // OFF - Routes may use default exports without a name
595
};
596
}
597
```
598
599
### Test File Overrides
600
601
Configuration applied specifically to test files when using jest-testing-library config.
602
603
```javascript { .api }
604
// Files: ["**/__tests__/**/*", "**/*.{spec,test}.*"]
605
interface TestOverride {
606
env: {
607
"jest/globals": true;
608
};
609
rules: {
610
// Jest, Jest DOM, and Testing Library rules applied here
611
};
612
}
613
```
614
615
## Installation Requirements
616
617
### Base Requirements
618
619
```javascript { .api }
620
interface PeerDependencies {
621
eslint: "^8.0.0";
622
react: "^18.0.0";
623
typescript?: "^5.1.0"; // Optional
624
}
625
626
interface Engines {
627
node: ">=18.0.0";
628
}
629
```
630
631
### Testing Configuration Requirements
632
633
Additional dependencies needed when using `jest-testing-library` configuration:
634
635
```json
636
{
637
"dependencies": {
638
"@testing-library/jest-dom": ">=5.16.0",
639
"@testing-library/react": ">=12.0.0",
640
"jest": ">=28.0.0"
641
}
642
}
643
```
644
645
## Error Handling
646
647
The package includes proper error handling through:
648
649
- **ESLint Configuration Validation**: Invalid configurations will be caught by ESLint
650
- **Peer Dependency Warnings**: Missing peer dependencies will generate npm warnings
651
- **Rule Conflicts**: Rule conflicts between configurations are resolved through ESLint's extending mechanism
652
- **Deprecation Warnings**: Console warning displayed when using the package about upcoming deprecation
653
654
### Deprecation Warning
655
656
When using this package, a deprecation warning will be displayed:
657
658
```
659
⚠️ REMIX FUTURE CHANGE: The `@remix-run/eslint-config` package is deprecated
660
and will not be included in React Router v7. We recommend moving towards a
661
streamlined ESLint config such as the ones included in the Remix templates.
662
See https://github.com/remix-run/remix/blob/main/templates/remix/.eslintrc.cjs.
663
```
664
665
This warning is shown every time the configuration is loaded, helping users transition to alternative ESLint configurations before React Router v7.
666
667
## Types
668
669
```typescript { .api }
670
// ESLint Rule Severity Levels
671
type RuleSeverity = 0 | 1 | 2 | "off" | "warn" | "error";
672
673
// Rule Configuration
674
type RuleConfig = RuleSeverity | [RuleSeverity, ...any[]];
675
676
// ESLint Configuration Object
677
interface ESLintConfiguration {
678
root?: boolean;
679
extends?: string | string[];
680
parser?: string;
681
parserOptions?: Record<string, any>;
682
env?: Record<string, boolean>;
683
plugins?: string[];
684
settings?: Record<string, any>;
685
rules?: Record<string, RuleConfig>;
686
overrides?: Array<{
687
files: string | string[];
688
excludedFiles?: string | string[];
689
extends?: string | string[];
690
parser?: string;
691
parserOptions?: Record<string, any>;
692
env?: Record<string, boolean>;
693
plugins?: string[];
694
rules?: Record<string, RuleConfig>;
695
}>;
696
}
697
```