diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c09c4b..552fbd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,23 @@ export default observer(Foo);
Thanks @HorusGoul!
+### Add recommended config and simple types (#67)
+
+You can now add the recommended config to your ESLint config like this:
+
+```js
+import reactRefresh from "eslint-plugin-react-refresh";
+
+export default [
+ /* Main config */
+ reactRefresh.configs.recommended, // Or reactRefresh.configs.vite for Vite users
+];
+```
+
+To follow ESLint recommandations, the rule is added with the `error` severity.
+
+Some simple types ensure that people typecheking their config won't need `@ts-expect-error` anymore.
+
### Bump ESLint peer dependency to 8.40
This was actually done by mistake in the previous release when moving from a deprecated API to a new one.
diff --git a/README.md b/README.md
index 5256595..dad9549 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
# eslint-plugin-react-refresh [](https://www.npmjs.com/package/eslint-plugin-react-refresh)
-Validate that your components can safely be updated with fast refresh.
+Validate that your components can safely be updated with Fast Refresh.
## Explainer
-"Fast refresh", also known as "hot reloading", is a feature in many modern bundlers.
+"Fast Refresh", also known as "hot reloading", is a feature in many modern bundlers.
If you update some React component(s) on disk, then the bundler will know to update only the impacted parts of your page -- without a full page reload.
`eslint-plugin-react-refresh` enforces that your components are structured in a way that integrations such as [react-refresh](https://www.npmjs.com/package/react-refresh) expect.
@@ -28,7 +28,33 @@ npm i -D eslint-plugin-react-refresh
## Usage
-This plugin provides a single rule, `react-refresh/only-export-components`.
+This plugin provides a single rule, `react-refresh/only-export-components`. There are multiple ways to enable it.
+
+### Recommended config
+
+```js
+import reactRefresh from "eslint-plugin-react-refresh";
+
+export default [
+ /* Main config */
+ reactRefresh.configs.recommended,
+];
+```
+
+### Vite config
+
+This enables the `allowConstantExport` option which is supported by Vite React plugins.
+
+```js
+import reactRefresh from "eslint-plugin-react-refresh";
+
+export default [
+ /* Main config */
+ reactRefresh.configs.vite,
+];
+```
+
+### Without config
```js
import reactRefresh from "eslint-plugin-react-refresh";
@@ -111,7 +137,6 @@ These options are all present on `react-refresh/only-exports-components`.
```ts
interface Options {
- allowExportNames?: string[];
allowExportNames?: string[];
allowConstantExport?: boolean;
customHOCs?: string[];
@@ -145,7 +170,7 @@ Example for [Remix](https://remix.run/docs/en/main/discussion/hot-module-replace
### allowConstantExport (v0.4.0)
-> Default: `false`
+> Default: `false` (`true` in `vite` config)
Don't warn when a constant (string, number, boolean, templateLiteral) is exported aside one or more components.
diff --git a/bun.lockb b/bun.lockb
index dd12af0..54b4d01 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/eslint.config.js b/eslint.config.js
index 95d0741..e7ef99c 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -1,3 +1,10 @@
import baseConfig from "@arnaud-barre/eslint-config";
-export default baseConfig;
+export default [
+ ...baseConfig,
+ {
+ rules: {
+ "@arnaud-barre/no-default-export": "off",
+ },
+ },
+];
diff --git a/package.json b/package.json
index 493c26a..a2c3aa4 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"lint": "bun eslint . --max-warnings 0",
"prettier": "bun prettier-ci --write",
"prettier-ci": "prettier --ignore-path=.gitignore --check '**/*.{js,ts,json,md,yml}'",
- "ci": "tsc && bun lint && bun prettier-ci && bun test && bun run build"
+ "ci": "tsc && bun lint && bun prettier-ci && bun test && bun run build && cd dist && publint"
},
"prettier": {},
"peerDependencies": {
@@ -25,6 +25,7 @@
"bun-types": "^1.1.31",
"eslint": "^9.13.0",
"prettier": "3.0.3",
+ "publint": "^0.2.12",
"typescript": "~5.6"
}
}
diff --git a/scripts/bundle.ts b/scripts/bundle.ts
index 1a9c064..f33a7d4 100755
--- a/scripts/bundle.ts
+++ b/scripts/bundle.ts
@@ -17,6 +17,7 @@ await build({
});
execSync("cp LICENSE README.md dist/");
+execSync("cp src/types.ts dist/index.d.ts");
writeFileSync(
"dist/package.json",
@@ -26,10 +27,12 @@ writeFileSync(
description:
"Validate that your components can safely be updated with Fast Refresh",
version: packageJSON.version,
+ type: "commonjs",
author: "Arnaud Barré (https://github.com/ArnaudBarre)",
license: packageJSON.license,
repository: "github:ArnaudBarre/eslint-plugin-react-refresh",
main: "index.js",
+ types: "index.d.ts",
keywords: [
"eslint",
"eslint-plugin",
diff --git a/src/index.ts b/src/index.ts
index e352741..ef12772 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,7 +1,26 @@
import { onlyExportComponents } from "./only-export-components.ts";
-export const rules = {
- "only-export-components": onlyExportComponents,
+const plugin = {
+ rules: {
+ "only-export-components": onlyExportComponents,
+ },
+};
+
+export default {
+ rules: plugin.rules,
+ configs: {
+ recommended: {
+ plugins: { "react-refresh": plugin },
+ rules: { "react-refresh/only-export-components": "error" },
+ },
+ vite: {
+ plugins: { "react-refresh": plugin },
+ rules: {
+ "react-refresh/only-export-components": [
+ "error",
+ { allowConstantExport: true },
+ ],
+ },
+ },
+ },
};
-// eslint-disable-next-line @arnaud-barre/no-default-export
-export default { rules };
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..d3deec2
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,14 @@
+type Config = {
+ plugins: { "react-refresh": { rules: Record } };
+ rules: Record;
+};
+
+declare const _default: {
+ rules: Record;
+ configs: {
+ recommended: Config;
+ vite: Config;
+ };
+};
+
+export default _default;
diff --git a/yarn.lock b/yarn.lock
index c7dd1b7..c80c2ee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1,6 +1,6 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
-# bun ./bun.lockb --hash: 0E2E8C648B832E19-f03f14fd57c82352-DE4F31B62A420BFD-c372ca07548ab99e
+# bun ./bun.lockb --hash: 200298EB1539E3D4-a461114ade976598-38DCF0097CD18ABB-5c9d03f73b5bf09b
"@arnaud-barre/eslint-config@^5.1.0":
@@ -633,7 +633,7 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
-eslint@>=7, eslint@>=8.56.0, "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.57.0 || ^9.0.0", eslint@^9.13.0:
+eslint@>=7, eslint@>=8.40, eslint@>=8.56.0, "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.57.0 || ^9.0.0", eslint@^9.13.0:
version "9.13.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz"
integrity sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==
@@ -833,11 +833,27 @@ flatted@^3.2.9:
resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz"
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+glob@^8.0.1:
+ version "8.1.0"
+ resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz"
+ integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
+ dependencies:
+ once "^1.3.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^5.0.1"
+ fs.realpath "^1.0.0"
+
glob-parent@^5.1.2:
version "5.1.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
@@ -894,6 +910,13 @@ ignore@^5.2.0, ignore@^5.3.1:
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+ignore-walk@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz"
+ integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==
+ dependencies:
+ minimatch "^5.0.1"
+
import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz"
@@ -912,6 +935,19 @@ indent-string@^4.0.0:
resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz"
@@ -1059,6 +1095,13 @@ minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
+minimatch@^5.0.1:
+ version "5.1.6"
+ resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz"
+ integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+ dependencies:
+ brace-expansion "^2.0.1"
+
minimatch@^9.0.4:
version "9.0.5"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz"
@@ -1066,6 +1109,11 @@ minimatch@^9.0.4:
dependencies:
brace-expansion "^2.0.1"
+mri@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz"
+ integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
+
ms@^2.1.3:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
@@ -1091,6 +1139,35 @@ normalize-package-data@^2.5.0:
hosted-git-info "^2.1.4"
validate-npm-package-license "^3.0.1"
+npm-bundled@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz"
+ integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==
+ dependencies:
+ npm-normalize-package-bin "^2.0.0"
+
+npm-normalize-package-bin@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz"
+ integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==
+
+npm-packlist@^5.1.3:
+ version "5.1.3"
+ resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz"
+ integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==
+ dependencies:
+ glob "^8.0.1"
+ ignore-walk "^5.0.1"
+ npm-bundled "^2.0.0"
+ npm-normalize-package-bin "^2.0.0"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
optionator@^0.9.3:
version "0.9.4"
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz"
@@ -1168,7 +1245,7 @@ path-parse@^1.0.7:
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-picocolors@^1.0.0, picocolors@^1.1.0:
+picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
@@ -1193,6 +1270,15 @@ prettier@3.0.3:
resolved "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz"
integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
+publint@^0.2.12:
+ version "0.2.12"
+ resolved "https://registry.npmjs.org/publint/-/publint-0.2.12.tgz"
+ integrity sha512-YNeUtCVeM4j9nDiTT2OPczmlyzOkIXNtdDZnSuajAxS/nZ6j3t7Vs9SUB4euQNddiltIwu7Tdd3s+hr08fAsMw==
+ dependencies:
+ npm-packlist "^5.1.3"
+ picocolors "^1.1.1"
+ sade "^1.8.1"
+
punycode@^2.1.0:
version "2.3.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
@@ -1260,6 +1346,13 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+sade@^1.8.1:
+ version "1.8.1"
+ resolved "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz"
+ integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
+ dependencies:
+ mri "^1.1.0"
+
"semver@2 || 3 || 4 || 5":
version "5.7.2"
resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz"
@@ -1432,6 +1525,11 @@ word-wrap@^1.2.5:
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"