From e90e5062e821b5004b48a6dd3657862c43664135 Mon Sep 17 00:00:00 2001 From: jimmy-guzman Date: Tue, 26 Nov 2024 18:03:01 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20`eslint-plugin-storyb?= =?UTF-8?q?ook`=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + package.json | 2 + pnpm-lock.yaml | 189 ++++++++++++++++++++++++++++++++ src/configs/storybook.spec.ts | 35 ++++++ src/configs/storybook.ts | 29 +++++ src/factory.spec.ts | 21 ++++ src/factory.ts | 5 + src/types.ts | 5 + src/utils/has-dependency.ts | 4 + src/utils/warnings-as-errors.ts | 9 ++ 10 files changed, 300 insertions(+) create mode 100644 src/configs/storybook.spec.ts create mode 100644 src/configs/storybook.ts create mode 100644 src/utils/warnings-as-errors.ts diff --git a/README.md b/README.md index 6c22ddd..2e2595b 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ export default eslintConfig({ jest: false, playwright: false, react: false, + storybook: false, tanstackQuery: false, testingLibrary: false, typescript: false, diff --git a/package.json b/package.json index 34862bc..6a6ef6b 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "0.4.14", "eslint-plugin-regexp": "^2.7.0", + "eslint-plugin-storybook": "0.11.1", "eslint-plugin-testing-library": "^7.0.0", "eslint-plugin-unicorn": "^56.0.1", "globals": "^15.12.0", @@ -105,6 +106,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "semantic-release": "24.2.0", + "storybook": "8.4.5", "tsx": "4.19.2", "typescript": "5.7.2", "vitest": "2.1.5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f13beae..30a538c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: eslint-plugin-regexp: specifier: ^2.7.0 version: 2.7.0(eslint@9.15.0(jiti@2.4.0)) + eslint-plugin-storybook: + specifier: 0.11.1 + version: 0.11.1(eslint@9.15.0(jiti@2.4.0))(typescript@5.7.2) eslint-plugin-testing-library: specifier: ^7.0.0 version: 7.0.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.7.2) @@ -177,6 +180,9 @@ importers: semantic-release: specifier: 24.2.0 version: 24.2.0(typescript@5.7.2) + storybook: + specifier: 8.4.5 + version: 8.4.5(prettier@3.3.3) tsx: specifier: 4.19.2 version: 4.19.2 @@ -1437,6 +1443,17 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@storybook/core@8.4.5': + resolution: {integrity: sha512-aB1sQNX5nRoUAqg5u1py0MuR/VPd6c6PhECa4rW6pmr7kZcfyP4PP6UFpXuN71ypTQlkRE3Vc5PQZ3gLhE9o3g==} + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + + '@storybook/csf@0.1.11': + resolution: {integrity: sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==} + '@tanstack/eslint-plugin-query@5.61.3': resolution: {integrity: sha512-tcP6xfoi/22oylcIUCWnfsfSpHZO/2MNq1pzex5NuwbeqEU2TQYJyoUp0Rdmtx9Pl9csPSNK70gBcCjuPQ0+ng==} peerDependencies: @@ -1811,6 +1828,10 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + astro-eslint-parser@1.1.0: resolution: {integrity: sha512-F6NW1RJo5pp2kPnnM97M5Ohw8zAGjv83MpxHqfAochH68n/kiXN57+hYaNUCA7XkScoVNr6yzvly3hsY34TGfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1850,6 +1871,10 @@ packages: before-after-hook@3.0.2: resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} + better-opn@3.0.2: + resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} + engines: {node: '>=12.0.0'} + bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} @@ -1867,6 +1892,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browser-assert@1.2.1: + resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} + browserslist@4.24.2: resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -2216,6 +2244,10 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -2363,6 +2395,11 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -2537,6 +2574,12 @@ packages: peerDependencies: eslint: '>=8.44.0' + eslint-plugin-storybook@0.11.1: + resolution: {integrity: sha512-yGKpAYkBm/Q2hZg476vRUAvd9lAccjjSvzU5nYy3BSQbKTPy7uopx7JEpwk2vSuw4weTMZzWF64z9/gp/K5RCg==} + engines: {node: '>= 18'} + peerDependencies: + eslint: '>=6' + eslint-plugin-testing-library@7.0.0: resolution: {integrity: sha512-Bwrn5Qi08Lf5Huv4ZGDNYxwkFLAyGQIPB9lC0ALlojymP32aKsSxWnccP1NvIcI5vMhkENg4Y5Td/Q9/tLYmGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, npm: '>=9.8.1'} @@ -3032,6 +3075,10 @@ packages: iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -3080,6 +3127,11 @@ packages: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3206,6 +3258,10 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + is-wsl@3.1.0: resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} engines: {node: '>=16'} @@ -3911,6 +3967,10 @@ packages: resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -4232,6 +4292,10 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -4297,6 +4361,10 @@ packages: resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} engines: {node: '>= 14.16.0'} + recast@0.23.9: + resolution: {integrity: sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==} + engines: {node: '>= 4'} + refa@0.12.1: resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -4595,6 +4663,15 @@ packages: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} + storybook@8.4.5: + resolution: {integrity: sha512-9tfgabXnMibYp3SvoaJXXMD63Pw0SA9Hnf5v6TxysCYZs4DZ/04fAkK+9RW+K4C5JkV83qXMMlrsPj766R47fg==} + hasBin: true + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + stream-combiner2@1.1.1: resolution: {integrity: sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==} @@ -4742,6 +4819,9 @@ packages: resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} engines: {node: '>=12'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -4780,6 +4860,10 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + tsconfck@3.1.4: resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==} engines: {node: ^18 || >=20} @@ -4950,6 +5034,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -6334,6 +6421,30 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@storybook/core@8.4.5(prettier@3.3.3)': + dependencies: + '@storybook/csf': 0.1.11 + better-opn: 3.0.2 + browser-assert: 1.2.1 + esbuild: 0.24.0 + esbuild-register: 3.6.0(esbuild@0.24.0) + jsdoc-type-pratt-parser: 4.1.0 + process: 0.11.10 + recast: 0.23.9 + semver: 7.6.3 + util: 0.12.5 + ws: 8.18.0 + optionalDependencies: + prettier: 3.3.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@storybook/csf@0.1.11': + dependencies: + type-fest: 2.19.0 + '@tanstack/eslint-plugin-query@5.61.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.7.2)': dependencies: '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.7.2) @@ -6781,6 +6892,10 @@ snapshots: ast-types-flow@0.0.8: {} + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + astro-eslint-parser@1.1.0(typescript@5.7.2): dependencies: '@astrojs/compiler': 2.10.3 @@ -6900,6 +7015,10 @@ snapshots: before-after-hook@3.0.2: {} + better-opn@3.0.2: + dependencies: + open: 8.4.2 + bottleneck@2.19.5: {} boxen@8.0.1: @@ -6926,6 +7045,8 @@ snapshots: dependencies: fill-range: 7.1.1 + browser-assert@1.2.1: {} + browserslist@4.24.2: dependencies: caniuse-lite: 1.0.30001677 @@ -7250,6 +7371,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.0.1 + define-lazy-prop@2.0.0: {} + define-lazy-prop@3.0.0: {} define-properties@1.2.1: @@ -7438,6 +7561,13 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 + esbuild-register@3.6.0(esbuild@0.24.0): + dependencies: + debug: 4.3.7 + esbuild: 0.24.0 + transitivePeerDependencies: + - supports-color + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -7721,6 +7851,16 @@ snapshots: regexp-ast-analysis: 0.7.1 scslre: 0.3.0 + eslint-plugin-storybook@0.11.1(eslint@9.15.0(jiti@2.4.0))(typescript@5.7.2): + dependencies: + '@storybook/csf': 0.1.11 + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.7.2) + eslint: 9.15.0(jiti@2.4.0) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-testing-library@7.0.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.7.2): dependencies: '@typescript-eslint/scope-manager': 8.15.0 @@ -8345,6 +8485,11 @@ snapshots: iron-webcrypto@1.2.1: {} + is-arguments@1.1.1: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -8394,6 +8539,8 @@ snapshots: dependencies: has-tostringtag: 1.0.2 + is-docker@2.2.1: {} + is-docker@3.0.0: {} is-extendable@0.1.1: {} @@ -8488,6 +8635,10 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + is-wsl@3.1.0: dependencies: is-inside-container: 1.0.0 @@ -9272,6 +9423,12 @@ snapshots: is-inside-container: 1.0.0 is-wsl: 3.1.0 + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -9531,6 +9688,8 @@ snapshots: process-nextick-args@2.0.1: {} + process@0.11.10: {} + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -9612,6 +9771,14 @@ snapshots: readdirp@4.0.2: {} + recast@0.23.9: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + refa@0.12.1: dependencies: '@eslint-community/regexpp': 4.12.1 @@ -10047,6 +10214,16 @@ snapshots: stdin-discarder@0.2.2: {} + storybook@8.4.5(prettier@3.3.3): + dependencies: + '@storybook/core': 8.4.5(prettier@3.3.3) + optionalDependencies: + prettier: 3.3.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + stream-combiner2@1.1.1: dependencies: duplexer2: 0.1.4 @@ -10217,6 +10394,8 @@ snapshots: dependencies: convert-hrtime: 5.0.0 + tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} tinyexec@0.3.1: {} @@ -10241,6 +10420,8 @@ snapshots: dependencies: typescript: 5.7.2 + ts-dedent@2.2.0: {} + tsconfck@3.1.4(typescript@5.7.2): optionalDependencies: typescript: 5.7.2 @@ -10421,6 +10602,14 @@ snapshots: util-deprecate@1.0.2: {} + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.13 + which-typed-array: 1.1.15 + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 diff --git a/src/configs/storybook.spec.ts b/src/configs/storybook.spec.ts new file mode 100644 index 0000000..19b5bf5 --- /dev/null +++ b/src/configs/storybook.spec.ts @@ -0,0 +1,35 @@ +import { storybookConfig } from "./storybook"; + +describe("storybook", () => { + it("should create stories rules", async () => { + const [_setup, storiesConfig] = await storybookConfig(); + + expect(storiesConfig?.rules).toMatchInlineSnapshot(` + { + "import-x/no-anonymous-default-export": "off", + "import/no-anonymous-default-export": "off", + "react-hooks/rules-of-hooks": "off", + "storybook/await-interactions": "error", + "storybook/context-in-play-function": "error", + "storybook/default-exports": "error", + "storybook/hierarchy-separator": "error", + "storybook/no-redundant-story-name": "error", + "storybook/prefer-pascal-case": "error", + "storybook/story-exports": "error", + "storybook/use-storybook-expect": "error", + "storybook/use-storybook-testing-library": "error", + "unicorn/no-anonymous-default-export": "off", + } + `); + }); + + it("should create main rules", async () => { + const [_setup, _storiesConfig, mainConfig] = await storybookConfig(); + + expect(mainConfig?.rules).toMatchInlineSnapshot(` + { + "storybook/no-uninstalled-addons": "error", + } + `); + }); +}); diff --git a/src/configs/storybook.ts b/src/configs/storybook.ts new file mode 100644 index 0000000..866f09a --- /dev/null +++ b/src/configs/storybook.ts @@ -0,0 +1,29 @@ +import { interopDefault } from "../utils/interop-default"; +import { warningAsErrors } from "../utils/warnings-as-errors"; + +export const storybookConfig = async () => { + const { configs } = await interopDefault(import("eslint-plugin-storybook")); + + const [setup, storiesConfig, mainConfig] = configs["flat/recommended"]; + + return [ + { + name: "jimmy.codes/storybook/setup", + plugins: setup?.plugins, + }, + { + files: storiesConfig?.files, + name: "jimmy.codes/storybook/stories-rules", + rules: { + ...warningAsErrors(storiesConfig?.rules), + "import-x/no-anonymous-default-export": "off", + "unicorn/no-anonymous-default-export": "off", + }, + }, + { + files: mainConfig?.files, + name: "jimmy.codes/storybook/main-rules", + rules: mainConfig?.rules, + }, + ]; +}; diff --git a/src/factory.spec.ts b/src/factory.spec.ts index 5602ac7..d15aee1 100644 --- a/src/factory.spec.ts +++ b/src/factory.spec.ts @@ -189,6 +189,15 @@ describe("eslintConfig", () => { expect(configs.at(7)?.name).toBe("jimmy.codes/react/query"); }); + it("should create configuration w/ storybook", async () => { + const configs = await eslintConfig({ + autoDetect: false, + storybook: true, + }); + + expect(configs.at(7)?.name).toBe("jimmy.codes/storybook/setup"); + }); + describe("autoDetect", () => { it("should include typescript when auto detection is enabled", async () => { vi.mocked(isPackageExists).mockImplementation((name) => { @@ -330,5 +339,17 @@ describe("eslintConfig", () => { ]), ); }); + + it("should include storybook when auto detection is enabled", async () => { + vi.mocked(isPackageExists).mockImplementation((name) => { + return name === "storybook"; + }); + + const configs = await eslintConfig({ + autoDetect: true, + }); + + expect(configs.at(7)?.name).toBe("jimmy.codes/storybook/setup"); + }); }); }); diff --git a/src/factory.ts b/src/factory.ts index 8a005e9..936a2dd 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -14,6 +14,7 @@ import { playwrightConfig } from "./configs/playwright"; import { prettierConfig } from "./configs/prettier"; import { reactConfig } from "./configs/react"; import { regexpConfig } from "./configs/regexp"; +import { storybookConfig } from "./configs/storybook"; import { tanstackQueryConfig } from "./configs/tanstack-query"; import { testingConfig } from "./configs/testing"; import { testingLibraryConfig } from "./configs/testing-library"; @@ -28,6 +29,7 @@ import { hasAstro, hasPlaywright, hasReact, + hasStorybook, hasTesting, hasTypescript, } from "./utils/has-dependency"; @@ -45,6 +47,7 @@ export const eslintConfig = async ( jest = false, playwright = false, react = false, + storybook = false, tanstackQuery = false, testing = false, testingLibrary = false, @@ -76,6 +79,7 @@ export const eslintConfig = async ( autoDetect, ); const isPlaywrightEnabled = playwright || (autoDetect && hasPlaywright()); + const isStorybookEnabled = storybook || (autoDetect && hasStorybook()); return [ javascriptConfig(), @@ -92,6 +96,7 @@ export const eslintConfig = async ( isTestingEnabled ? await testingConfig(testingOptions, autoDetect) : [], isTestingLibraryEnabled ? await testingLibraryConfig() : [], isPlaywrightEnabled ? await playwrightConfig() : [], + isStorybookEnabled ? await storybookConfig() : [], prettierConfig(), commonjsConfig(), ignoresConfig(ignores), diff --git a/src/types.ts b/src/types.ts index 56fc39c..9bd1369 100644 --- a/src/types.ts +++ b/src/types.ts @@ -96,6 +96,11 @@ export interface Options { * @default false */ react?: boolean | ReactOptions; + /** + * Are Storybook rules enabled? + * @default false + */ + storybook?: boolean; /** * Are Tanstack Query rules enabled? * @default false diff --git a/src/utils/has-dependency.ts b/src/utils/has-dependency.ts index b5f8814..5bf904f 100644 --- a/src/utils/has-dependency.ts +++ b/src/utils/has-dependency.ts @@ -39,3 +39,7 @@ export const hasAstro = () => { export const hasPlaywright = () => { return isPackageExists("@playwright/test"); }; + +export const hasStorybook = () => { + return isPackageExists("storybook"); +}; diff --git a/src/utils/warnings-as-errors.ts b/src/utils/warnings-as-errors.ts new file mode 100644 index 0000000..0d3fad0 --- /dev/null +++ b/src/utils/warnings-as-errors.ts @@ -0,0 +1,9 @@ +import type { Linter } from "eslint"; + +export const warningAsErrors = (rules: Partial = {}) => { + return Object.fromEntries( + Object.entries(rules).map(([rule, option]) => { + return [rule, option === "warn" ? "error" : option]; + }), + ); +};