diff --git a/package-lock.json b/package-lock.json index 6dcdcd3b..52b14ca2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -120,7 +120,7 @@ "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", - "optional": true, + "devOptional": true, "peer": true, "engines": { "node": ">=6.9.0" @@ -130,7 +130,7 @@ "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -161,14 +161,14 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "optional": true, + "devOptional": true, "peer": true }, "node_modules/@babel/core/node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "optional": true, + "devOptional": true, "peer": true, "bin": { "json5": "lib/cli.js" @@ -181,17 +181,53 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "optional": true, + "devOptional": true, "peer": true, "bin": { "semver": "bin/semver.js" } }, + "node_modules/@babel/eslint-parser": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.15.tgz", + "integrity": "sha512-yc8OOBIQk1EcRrpizuARSQS0TWAcOMpEJ1aafhNznaeYkeL+OhqnDObGFylB8ka8VFF/sZc+S4RzHyO+3LjQxg==", + "dev": true, + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/types": "^7.23.0", @@ -207,7 +243,7 @@ "version": "0.3.18", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", @@ -218,7 +254,7 @@ "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/compat-data": "^7.22.9", @@ -238,7 +274,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "yallist": "^3.0.2" @@ -248,7 +284,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "optional": true, + "devOptional": true, "peer": true, "bin": { "semver": "bin/semver.js" @@ -258,14 +294,14 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "optional": true, + "devOptional": true, "peer": true }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "optional": true, + "devOptional": true, "peer": true, "engines": { "node": ">=6.9.0" @@ -275,7 +311,7 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/template": "^7.22.15", @@ -289,7 +325,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/types": "^7.22.5" @@ -302,7 +338,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/types": "^7.22.5" @@ -315,7 +351,7 @@ "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", @@ -345,7 +381,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/types": "^7.22.5" @@ -358,7 +394,7 @@ "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/types": "^7.22.5" @@ -371,7 +407,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "optional": true, + "devOptional": true, "peer": true, "engines": { "node": ">=6.9.0" @@ -389,7 +425,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "optional": true, + "devOptional": true, "peer": true, "engines": { "node": ">=6.9.0" @@ -399,7 +435,7 @@ "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/template": "^7.22.5", @@ -483,7 +519,7 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "optional": true, + "devOptional": true, "peer": true, "bin": { "parser": "bin/babel-parser.js" @@ -698,7 +734,7 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/code-frame": "^7.22.13", @@ -713,7 +749,7 @@ "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/code-frame": "^7.22.13", @@ -735,7 +771,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "optional": true, + "devOptional": true, "peer": true, "engines": { "node": ">=4" @@ -745,7 +781,7 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "optional": true, + "devOptional": true, "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", @@ -2521,6 +2557,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", @@ -3318,6 +3363,10 @@ "resolved": "packages/eslint-config", "link": true }, + "node_modules/@readme/eslint-plugin": { + "resolved": "packages/eslint-plugin", + "link": true + }, "node_modules/@readme/oas-examples": { "version": "5.12.0", "resolved": "https://registry.npmjs.org/@readme/oas-examples/-/oas-examples-5.12.0.tgz", @@ -6480,6 +6529,7 @@ "version": "4.21.10", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -6494,7 +6544,6 @@ "url": "https://github.com/sponsors/ai" } ], - "optional": true, "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001517", @@ -6772,6 +6821,7 @@ "version": "1.0.30001518", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz", "integrity": "sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -6786,7 +6836,6 @@ "url": "https://github.com/sponsors/ai" } ], - "optional": true, "peer": true }, "node_modules/ccount": { @@ -7925,7 +7974,7 @@ "version": "1.4.480", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.480.tgz", "integrity": "sha512-IXTgg+bITkQv/FLP9FjX6f9KFCs5hQWeh5uNSKxB9mqYj/JXhHDbu+ekS43LVvbkL3eW6/oZy4+r9Om6lan1Uw==", - "optional": true, + "devOptional": true, "peer": true }, "node_modules/emittery": { @@ -9757,7 +9806,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "optional": true, + "devOptional": true, "peer": true, "engines": { "node": ">=6.9.0" @@ -12690,7 +12739,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "optional": true, + "devOptional": true, "peer": true, "bin": { "jsesc": "bin/jsesc" @@ -15368,7 +15417,7 @@ "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "optional": true, + "devOptional": true, "peer": true }, "node_modules/nopt": { @@ -19539,7 +19588,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "optional": true, + "devOptional": true, "peer": true, "engines": { "node": ">=4" @@ -20346,6 +20395,7 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -20360,7 +20410,6 @@ "url": "https://github.com/sponsors/ai" } ], - "optional": true, "peer": true, "dependencies": { "escalade": "^3.1.1", @@ -21350,6 +21399,7 @@ "version": "13.2.0", "license": "ISC", "dependencies": { + "@readme/eslint-plugin": "file:../eslint-plugin", "@typescript-eslint/eslint-plugin": "^6.2.1", "@typescript-eslint/parser": "^6.2.1", "eslint-config-airbnb-base": "^15.0.0", @@ -21404,6 +21454,22 @@ "typescript": "^3 || ^4 || ^5" } }, + "packages/eslint-plugin": { + "name": "@readme/eslint-plugin", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@babel/eslint-parser": "^7.22.15", + "@readme/eslint-config": "file:../eslint-config", + "vitest": "^0.34.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, "packages/spectral-config": { "name": "@readme/spectral-config", "version": "4.0.3", diff --git a/package.json b/package.json index 58e3ccfe..108b29f3 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "lint": "npm run prettier && npm run lint:js", "lint:js": "lerna run lint --stream", "prettier": "prettier --check .", + "prettier:write": "prettier --list-different --write \"./**/**.js\"", "prepare": "husky install", "version": "npm i && git add package-lock.json", "release": "lerna publish", diff --git a/packages/eslint-config/esm.js b/packages/eslint-config/esm.js index c787454e..c470ebe5 100644 --- a/packages/eslint-config/esm.js +++ b/packages/eslint-config/esm.js @@ -1,22 +1,12 @@ /** @type {import("eslint-define-config").ESLintConfig} */ const config = { - extends: ['plugin:require-extensions/recommended'], - plugins: ['require-extensions', 'unicorn'], + extends: ['plugin:require-extensions/recommended', 'plugin:@readme/esm'], + plugins: ['require-extensions', 'unicorn', '@readme'], rules: { // see here for more rules to possibly enable in the future: // https://gist.github.com/Jaid/164668c0151ae09d2bc81be78a203dd5 'import/no-commonjs': 'error', - /** - * This rule enablement is generally only intended for public-facing libraries but when shipping - * a package that exports CJS and ESM exports you cannot do `export default` and also - * `export type` in the same file because `export default` gets translated to `export =` in CJS - * and TS types can't be layered on top of that. - * - * @see {@link https://github.com/isaacs/tshy#handling-default-exports} - */ - 'import/no-default-export': 'warn', - 'node/no-extraneous-import': 'error', 'unicorn/prefer-module': 'error', 'unicorn/prefer-node-protocol': 'error', diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 66e0ce8f..81210394 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -9,18 +9,19 @@ "type": "git", "url": "git@github.com:readmeio/standards.git" }, + "bugs": { + "url": "https://github.com/readmeio/standards/issues" + }, + "homepage": "https://github.com/readmeio/standards#readme", "engines": { "node": ">=18" }, "scripts": { - "prettier": "prettier --list-different --write \"./**/**.js\"", "lint": "eslint .", "test": "tsc" }, - "publishConfig": { - "access": "public" - }, "dependencies": { + "@readme/eslint-plugin": "file:../eslint-plugin", "@typescript-eslint/eslint-plugin": "^6.2.1", "@typescript-eslint/parser": "^6.2.1", "eslint-config-airbnb-base": "^15.0.0", diff --git a/packages/eslint-plugin/.eslintrc b/packages/eslint-plugin/.eslintrc new file mode 100644 index 00000000..910f522f --- /dev/null +++ b/packages/eslint-plugin/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "@readme/eslint-config", + "parserOptions": { + "ecmaVersion": 2022 + } +} diff --git a/packages/eslint-plugin/LICENSE.md b/packages/eslint-plugin/LICENSE.md new file mode 100644 index 00000000..9cc818cf --- /dev/null +++ b/packages/eslint-plugin/LICENSE.md @@ -0,0 +1,5 @@ +Copyright 2023 ReadMe + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED β€œAS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md new file mode 100644 index 00000000..fac5b142 --- /dev/null +++ b/packages/eslint-plugin/README.md @@ -0,0 +1,36 @@ +# eslint-plugin-readme + +Custom ESLint plugin for some ReadMe engineering guidelines and gotchas. + +[![](https://raw.githubusercontent.com/readmeio/.github/main/oss-header.png)](https://readme.io) + +[![npm](https://img.shields.io/npm/v/@readme/eslint-plugin)](https://npm.im/@readme/eslint-plugin) [![Build](https://github.com/readmeio/standards/workflows/CI/badge.svg)](https://github.com/readmeio/standards) + +## Usage + +In `.eslintrc` file add the following line: + +```js +extends: ['plugin:@readme/'], +plugins: ['@readme'], +``` + +## πŸ”– Available Configs + + + +| Config | Description | +| :--- | :--- | +| `esm` | Rules specific to ESM libraries. | + + + +## πŸ“– Rules + + + +| Rule | Description | Config | +| :--- | :--- | :--- | +| [no-dual-exports](https://github.com/readmeio/standards/tree/main/packages/eslint-plugin/docs/no-dual-exports.md) | Prevent cases of having a file with dual `default` and named exports. | `esm` | + + diff --git a/packages/eslint-plugin/docs/no-dual-exports.md b/packages/eslint-plugin/docs/no-dual-exports.md new file mode 100644 index 00000000..246dc0cd --- /dev/null +++ b/packages/eslint-plugin/docs/no-dual-exports.md @@ -0,0 +1,33 @@ +# Prevent cases of having a file with dual `default` and named exports + +In libraries that support CJS and ESM environments, having a file that has a `default` **and** a named export makes that file incompatible with CJS environments due to the way that CJS resolution works in that case. To resolve this, a file should either have a single `default` export, or it should only be comprised of named exports. You cannot have both. + +## Fail + +```ts +export interface ReducerOptions { + paths?: Record; + tags?: string[]; +} + +export function reducer(definition: OASDocument, opts: ReducerOptions = {}) { + /** code here */ +} +``` + +```js +export default class SDK {} +``` + +## Pass + +```ts +export interface ReducerOptions { + paths?: Record; + tags?: string[]; +} + +export default function reducer(definition: OASDocument, opts: ReducerOptions = {}) { + /** code here */ +} +``` diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js new file mode 100644 index 00000000..b18fccc7 --- /dev/null +++ b/packages/eslint-plugin/index.js @@ -0,0 +1,20 @@ +/* eslint-disable global-require */ +const { name: packageName, version: packageVersion } = require('./package.json'); + +module.exports = { + meta: { + name: packageName, + version: packageVersion, + }, + configs: { + esm: { + plugins: ['readme'], + rules: { + 'readme/no-dual-exports': 'error', + }, + }, + }, + rules: { + 'no-dual-exports': require('./rules/no-dual-exports'), + }, +}; diff --git a/packages/eslint-plugin/lib/utils.js b/packages/eslint-plugin/lib/utils.js new file mode 100644 index 00000000..f12fa3d4 --- /dev/null +++ b/packages/eslint-plugin/lib/utils.js @@ -0,0 +1,5 @@ +module.exports = { + getDocURL: rule => { + return `https://github.com/readmeio/standards/tree/main/packages/eslint-plugin/docs/${rule}.md`; + }, +}; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json new file mode 100644 index 00000000..94a5ad0d --- /dev/null +++ b/packages/eslint-plugin/package.json @@ -0,0 +1,32 @@ +{ + "name": "@readme/eslint-plugin", + "version": "1.0.0", + "description": "ESLint plugin providing custom rules for ReadMe's coding standards", + "main": "index.js", + "author": "Jon Ursenbach ", + "license": "ISC", + "repository": { + "type": "git", + "url": "git://github.com/readmeio/standards.git", + "directory": "packages/eslint-plugin" + }, + "bugs": { + "url": "https://github.com/readmeio/standards/issues" + }, + "homepage": "https://github.com/readmeio/standards#readme", + "engines": { + "node": ">=18" + }, + "scripts": { + "lint": "eslint .", + "test": "vitest run" + }, + "peerDependencies": { + "eslint": "^8.0.0" + }, + "devDependencies": { + "@babel/eslint-parser": "^7.22.15", + "@readme/eslint-config": "file:../eslint-config", + "vitest": "^0.34.6" + } +} diff --git a/packages/eslint-plugin/rules/no-dual-exports.js b/packages/eslint-plugin/rules/no-dual-exports.js new file mode 100644 index 00000000..0b7998dc --- /dev/null +++ b/packages/eslint-plugin/rules/no-dual-exports.js @@ -0,0 +1,29 @@ +const { getDocURL } = require('../lib/utils'); + +module.exports = { + meta: { + type: 'problem', + category: 'ESM', + docs: { + description: 'Prevent cases of having a file with dual `default` and named exports.', + url: getDocURL('no-dual-exports'), + }, + }, + create: context => { + return { + ExportDefaultDeclaration: node => { + if (!node?.parent?.body) { + return; + } else if (!node.parent.body.filter(n => n.type === 'ExportNamedDeclaration').length) { + return; + } + + context.report({ + node, + message: + 'In a dual package world you cannot ship a file for CJS environments that has a mix of `default` and named exports. This export should either be made a named export or refactor this file to have just one default export.', + }); + }, + }; + }, +}; diff --git a/packages/eslint-plugin/test/no-dual-exports.test.js b/packages/eslint-plugin/test/no-dual-exports.test.js new file mode 100644 index 00000000..7a919522 --- /dev/null +++ b/packages/eslint-plugin/test/no-dual-exports.test.js @@ -0,0 +1,47 @@ +const { RuleTester } = require('eslint'); + +const { rules } = require('..'); + +const ruleTester = new RuleTester({ + parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + requireConfigFile: false, + }, +}); + +const errorMessage = + 'In a dual package world you cannot ship a file for CJS environments that has a mix of `default` and named exports. This export should either be made a named export or refactor this file to have just one default export.'; + +ruleTester.run('no-dual-exports', rules['no-dual-exports'], { + valid: [ + { + code: 'export class ReducerOptions {}', + }, + { + code: 'export default class ReducerOptions {}', + }, + { + code: 'export default function reducer(definition, opts) {}', + }, + { + code: `export class ReducerOptions {} + + export function reducer(definition, opts) {}`, + }, + ], + invalid: [ + { + code: `export class ReducerOptions {} + + export default function reducer(definition, opts) {}`, + errors: [{ message: errorMessage }], + }, + { + code: `const buster = "buster"; + export function getDogName() {}; + + export default buster = buster;`, + errors: [{ message: errorMessage }], + }, + ], +}); diff --git a/packages/eslint-plugin/vitest.config.ts b/packages/eslint-plugin/vitest.config.ts new file mode 100644 index 00000000..7382f40e --- /dev/null +++ b/packages/eslint-plugin/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + }, +});