Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,22 @@ If your using JSX inside `.js` files (which I don't recommend because it forces
"react-refresh/only-export-components": ["warn", { "checkJS": true }]
}
```

### createContextMethods

The `createContextMethods` option allows you to specify custom context-creating functions for detection by the rule. By default, it only detects `createContext`, but you can add methods like `createMyContext` in your configuration.

```json
{
"react-refresh/only-export-components": [
"error",
{ "createContextMethods": ["createMyContext"] }
]
}
```

```jsx
export const MyComponent = () => <div />;
// ESLint error
export const MyContext = createMyContext();
```
25 changes: 25 additions & 0 deletions src/only-export-components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,21 @@ const valid = [
name: "Only React context",
code: "export const MyContext = createContext('test');",
},
{
name: "Component and local React Context from other module",
code: "export const MyComponent = () => {}; const MyContext = createMyContext('test');",
errorId: "reactContext",
options: [
{
createContextMethods: ["createMyContext"],
},
],
},
{
name: "Component and React Context from other module without options",
code: "export const MyComponent = () => {}; export const MyContext = createMyContext('test');",
errorId: "reactContext",
},
];

const invalid = [
Expand Down Expand Up @@ -295,6 +310,16 @@ const invalid = [
code: "export const MyComponent = () => {}; export const MyContext = React.createContext('test');",
errorId: "reactContext",
},
{
name: "Component and React Context from other module",
code: "export const MyComponent = () => {}; export const MyContext = createMyContext('test');",
errorId: "reactContext",
options: [
{
createContextMethods: ["createMyContext"],
},
],
},
];

const it = (name: string, cases: Parameters<typeof ruleTester.run>[2]) => {
Expand Down
12 changes: 10 additions & 2 deletions src/only-export-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const onlyExportComponents: TSESLint.RuleModule<
allowConstantExport?: boolean;
checkJS?: boolean;
allowExportNames?: string[];
createContextMethods?: string[];
},
]
> = {
Expand All @@ -47,6 +48,7 @@ export const onlyExportComponents: TSESLint.RuleModule<
allowConstantExport: { type: "boolean" },
checkJS: { type: "boolean" },
allowExportNames: { type: "array", items: { type: "string" } },
createContextMethods: { type: "array", items: { type: "string" } },
},
additionalProperties: false,
},
Expand All @@ -58,6 +60,7 @@ export const onlyExportComponents: TSESLint.RuleModule<
allowConstantExport = false,
checkJS = false,
allowExportNames,
createContextMethods = [],
} = context.options[0] ?? {};
const filename = context.filename;
// Skip tests & stories files
Expand All @@ -79,6 +82,11 @@ export const onlyExportComponents: TSESLint.RuleModule<
? new Set(allowExportNames)
: undefined;

const createContextMethodsSet = new Set([
...createContextMethods,
"createContext",
]);

return {
Program(program) {
let hasExports = false;
Expand Down Expand Up @@ -133,10 +141,10 @@ export const onlyExportComponents: TSESLint.RuleModule<
init.type === "CallExpression" &&
// createContext || React.createContext
((init.callee.type === "Identifier" &&
init.callee.name === "createContext") ||
createContextMethodsSet.has(init.callee.name)) ||
(init.callee.type === "MemberExpression" &&
init.callee.property.type === "Identifier" &&
init.callee.property.name === "createContext"))
createContextMethodsSet.has(init.callee.property.name)))
) {
reactContextExports.push(identifierNode);
return;
Expand Down