diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d4f9848..3db7164 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,6 +42,7 @@ import { CategoryId } from '../utils/constants'
module.exports = {
meta: {
+ severity: 'error', // whether the rule should yield 'warn' or 'error'
docs: {
categories: [CategoryId.RECOMMENDED], // You should always use an existing category from the CategoryId enum], or create a new one there
excludeFromConfig: true, // If the rule is not ready to be shipped in any category, set this flag to true, otherwise remove it
@@ -82,7 +83,7 @@ ruleTester.run('my-rule-name', rule, {
When you make changes to rules or create/delete rules, the configuration files and documentation have to be updated. For that, run the following command:
```sh
-yarn update-all
+pnpm run update-all
```
### Useful resources
diff --git a/README.md b/README.md
index 490840e..ea0d498 100644
--- a/README.md
+++ b/README.md
@@ -175,7 +175,7 @@ This plugin does not support MDX files.
| [`storybook/no-uninstalled-addons`](./docs/rules/no-uninstalled-addons.md) | This rule identifies storybook addons that are invalid because they are either not installed or contain a typo in their name. | |
- recommended
- flat/recommended
|
| [`storybook/prefer-pascal-case`](./docs/rules/prefer-pascal-case.md) | Stories should use PascalCase | 🔧 | - recommended
- flat/recommended
|
| [`storybook/story-exports`](./docs/rules/story-exports.md) | A story file must contain at least one story export | | - recommended
- flat/recommended
- csf
- flat/csf
|
-| [`storybook/use-storybook-expect`](./docs/rules/use-storybook-expect.md) | Use expect from `@storybook/jest` | 🔧 | - addon-interactions
- flat/addon-interactions
- recommended
- flat/recommended
|
+| [`storybook/use-storybook-expect`](./docs/rules/use-storybook-expect.md) | Use expect from `@storybook/test` or `@storybook/jest` | 🔧 | - addon-interactions
- flat/addon-interactions
- recommended
- flat/recommended
|
| [`storybook/use-storybook-testing-library`](./docs/rules/use-storybook-testing-library.md) | Do not use testing-library directly on stories | 🔧 | - addon-interactions
- flat/addon-interactions
- recommended
- flat/recommended
|
diff --git a/lib/configs/flat/addon-interactions.ts b/lib/configs/flat/addon-interactions.ts
index 82d6aab..f0ed440 100644
--- a/lib/configs/flat/addon-interactions.ts
+++ b/lib/configs/flat/addon-interactions.ts
@@ -8,6 +8,7 @@ export = [
name: 'storybook:addon-interactions:setup',
plugins: {
get storybook() {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
return require('../../index')
},
},
diff --git a/lib/configs/flat/csf.ts b/lib/configs/flat/csf.ts
index 23384d1..b4f7faa 100644
--- a/lib/configs/flat/csf.ts
+++ b/lib/configs/flat/csf.ts
@@ -8,6 +8,7 @@ export = [
name: 'storybook:csf:setup',
plugins: {
get storybook() {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
return require('../../index')
},
},
diff --git a/lib/configs/flat/recommended.ts b/lib/configs/flat/recommended.ts
index 4dd59fe..5ece3b3 100644
--- a/lib/configs/flat/recommended.ts
+++ b/lib/configs/flat/recommended.ts
@@ -8,6 +8,7 @@ export = [
name: 'storybook:recommended:setup',
plugins: {
get storybook() {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
return require('../../index')
},
},
diff --git a/lib/rules/await-interactions.ts b/lib/rules/await-interactions.ts
index 13bc314..6650ec8 100644
--- a/lib/rules/await-interactions.ts
+++ b/lib/rules/await-interactions.ts
@@ -29,10 +29,10 @@ export = createStorybookRule({
name: 'await-interactions',
defaultOptions: [],
meta: {
+ severity: 'error',
docs: {
description: 'Interactions should be awaited',
categories: [CategoryId.ADDON_INTERACTIONS, CategoryId.RECOMMENDED],
- recommended: 'strict',
},
messages: {
interactionShouldBeAwaited: 'Interaction should be awaited: {{method}}',
diff --git a/lib/rules/context-in-play-function.ts b/lib/rules/context-in-play-function.ts
index c4c5da7..8288171 100644
--- a/lib/rules/context-in-play-function.ts
+++ b/lib/rules/context-in-play-function.ts
@@ -27,10 +27,10 @@ export = createStorybookRule({
defaultOptions: [],
meta: {
type: 'problem',
+ severity: 'error',
docs: {
description: 'Pass a context when invoking play function of another story',
categories: [CategoryId.RECOMMENDED, CategoryId.ADDON_INTERACTIONS],
- recommended: 'strict',
},
messages: {
passContextToPlayFunction: 'Pass a context when invoking play function of another story',
diff --git a/lib/rules/csf-component.ts b/lib/rules/csf-component.ts
index eb25b63..ea1729f 100644
--- a/lib/rules/csf-component.ts
+++ b/lib/rules/csf-component.ts
@@ -18,10 +18,10 @@ export = createStorybookRule({
defaultOptions: [],
meta: {
type: 'suggestion',
+ severity: 'warn',
docs: {
description: 'The component property should be set',
categories: [CategoryId.CSF],
- recommended: 'recommended',
},
messages: {
missingComponentProperty: 'Missing component property.',
diff --git a/lib/rules/default-exports.ts b/lib/rules/default-exports.ts
index f728223..e4c18ba 100644
--- a/lib/rules/default-exports.ts
+++ b/lib/rules/default-exports.ts
@@ -19,10 +19,10 @@ export = createStorybookRule({
defaultOptions: [],
meta: {
type: 'problem',
+ severity: 'error',
docs: {
description: 'Story files should have a default export',
categories: [CategoryId.CSF, CategoryId.RECOMMENDED],
- recommended: 'strict',
},
messages: {
shouldHaveDefaultExport: 'The file should have a default export.',
diff --git a/lib/rules/hierarchy-separator.ts b/lib/rules/hierarchy-separator.ts
index 0c35105..139e360 100644
--- a/lib/rules/hierarchy-separator.ts
+++ b/lib/rules/hierarchy-separator.ts
@@ -20,10 +20,10 @@ export = createStorybookRule({
type: 'problem',
fixable: 'code',
hasSuggestions: true,
+ severity: 'warn',
docs: {
description: 'Deprecated hierarchy separator in title property',
categories: [CategoryId.CSF, CategoryId.RECOMMENDED],
- recommended: 'recommended',
},
messages: {
useCorrectSeparators: 'Use correct separators',
diff --git a/lib/rules/meta-inline-properties.ts b/lib/rules/meta-inline-properties.ts
index 05c700d..97cc533 100644
--- a/lib/rules/meta-inline-properties.ts
+++ b/lib/rules/meta-inline-properties.ts
@@ -21,11 +21,11 @@ export = createStorybookRule({
defaultOptions: [{ csfVersion: 3 }],
meta: {
type: 'problem',
+ severity: 'error',
docs: {
description: 'Meta should only have inline properties',
categories: [CategoryId.CSF, CategoryId.RECOMMENDED],
excludeFromConfig: true,
- recommended: 'strict',
},
messages: {
metaShouldHaveInlineProperties: 'Meta should only have inline properties: {{property}}',
diff --git a/lib/rules/no-redundant-story-name.ts b/lib/rules/no-redundant-story-name.ts
index aab64a3..f5c98f8 100644
--- a/lib/rules/no-redundant-story-name.ts
+++ b/lib/rules/no-redundant-story-name.ts
@@ -29,10 +29,10 @@ export = createStorybookRule({
type: 'suggestion',
fixable: 'code',
hasSuggestions: true,
+ severity: 'warn',
docs: {
description: 'A story should not have a redundant name property',
categories: [CategoryId.CSF, CategoryId.RECOMMENDED],
- recommended: 'recommended',
},
messages: {
removeRedundantName: 'Remove redundant name',
diff --git a/lib/rules/no-stories-of.ts b/lib/rules/no-stories-of.ts
index 419716a..9616107 100644
--- a/lib/rules/no-stories-of.ts
+++ b/lib/rules/no-stories-of.ts
@@ -15,10 +15,10 @@ export = createStorybookRule({
defaultOptions: [],
meta: {
type: 'problem',
+ severity: 'error',
docs: {
description: 'storiesOf is deprecated and should not be used',
categories: [CategoryId.CSF_STRICT],
- recommended: 'strict',
},
messages: {
doNotUseStoriesOf: 'storiesOf is deprecated and should not be used',
diff --git a/lib/rules/no-title-property-in-meta.ts b/lib/rules/no-title-property-in-meta.ts
index 3a22e32..0a05f32 100644
--- a/lib/rules/no-title-property-in-meta.ts
+++ b/lib/rules/no-title-property-in-meta.ts
@@ -20,10 +20,10 @@ export = createStorybookRule({
type: 'problem',
fixable: 'code',
hasSuggestions: true,
+ severity: 'error',
docs: {
description: 'Do not define a title in meta',
categories: [CategoryId.CSF_STRICT],
- recommended: 'strict',
},
messages: {
removeTitleInMeta: 'Remove title property from meta',
diff --git a/lib/rules/no-uninstalled-addons.ts b/lib/rules/no-uninstalled-addons.ts
index 990c5a4..25365c4 100644
--- a/lib/rules/no-uninstalled-addons.ts
+++ b/lib/rules/no-uninstalled-addons.ts
@@ -35,11 +35,11 @@ export = createStorybookRule({
],
meta: {
type: 'problem',
+ severity: 'error',
docs: {
description:
'This rule identifies storybook addons that are invalid because they are either not installed or contain a typo in their name.',
categories: [CategoryId.RECOMMENDED],
- recommended: 'strict',
},
messages: {
addonIsNotInstalled: `The {{ addonName }} is not installed in {{packageJsonPath}}. Did you forget to install it or is your package.json in a different location?`,
@@ -146,7 +146,8 @@ export = createStorybookRule({
const parsedFile = JSON.parse(file)
packageJson.dependencies = parsedFile.dependencies || {}
packageJson.devDependencies = parsedFile.devDependencies || {}
- } catch (e) {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ } catch (err) {
throw new Error(
dedent`The provided path in your eslintrc.json - ${path} is not a valid path to a package.json file or your package.json file is not in the same folder as ESLint is running from.
diff --git a/lib/rules/prefer-pascal-case.ts b/lib/rules/prefer-pascal-case.ts
index fe06439..2422d80 100644
--- a/lib/rules/prefer-pascal-case.ts
+++ b/lib/rules/prefer-pascal-case.ts
@@ -22,10 +22,10 @@ export = createStorybookRule({
type: 'suggestion',
fixable: 'code',
hasSuggestions: true,
+ severity: 'warn',
docs: {
description: 'Stories should use PascalCase',
categories: [CategoryId.RECOMMENDED],
- recommended: 'stylistic',
},
messages: {
convertToPascalCase: 'Use pascal case',
@@ -137,6 +137,7 @@ export = createStorybookRule({
excludeStories: getDescriptor(meta, 'excludeStories'),
includeStories: getDescriptor(meta, 'includeStories'),
}
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (err) {
//
}
diff --git a/lib/rules/story-exports.ts b/lib/rules/story-exports.ts
index fea74f7..7993e5d 100644
--- a/lib/rules/story-exports.ts
+++ b/lib/rules/story-exports.ts
@@ -24,10 +24,10 @@ export = createStorybookRule({
defaultOptions: [],
meta: {
type: 'problem',
+ severity: 'error',
docs: {
description: 'A story file must contain at least one story export',
categories: [CategoryId.RECOMMENDED, CategoryId.CSF],
- recommended: 'strict',
},
messages: {
shouldHaveStoryExport: 'The file should have at least one story export',
@@ -69,6 +69,7 @@ export = createStorybookRule({
excludeStories: getDescriptor(meta, 'excludeStories'),
includeStories: getDescriptor(meta, 'includeStories'),
}
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (err) {
//
}
diff --git a/lib/rules/use-storybook-expect.ts b/lib/rules/use-storybook-expect.ts
index f2c3f9e..8c78ce0 100644
--- a/lib/rules/use-storybook-expect.ts
+++ b/lib/rules/use-storybook-expect.ts
@@ -23,17 +23,15 @@ export = createStorybookRule({
meta: {
type: 'suggestion',
fixable: 'code',
- hasSuggestions: true,
schema: [],
+ severity: 'error',
docs: {
description: 'Use expect from `@storybook/test` or `@storybook/jest`',
categories: [CategoryId.ADDON_INTERACTIONS, CategoryId.RECOMMENDED],
- recommended: 'strict',
},
messages: {
- updateImports: 'Update imports',
useExpectFromStorybook:
- 'Do not use global expect directly in the story. You should import it from `@storybook/test` or `@storybook/jest` instead.',
+ 'Do not use global expect directly in the story. You should import it from `@storybook/test` (preferrably) or `@storybook/jest` instead.',
},
},
diff --git a/lib/rules/use-storybook-testing-library.ts b/lib/rules/use-storybook-testing-library.ts
index b4430c2..07b09af 100644
--- a/lib/rules/use-storybook-testing-library.ts
+++ b/lib/rules/use-storybook-testing-library.ts
@@ -18,16 +18,16 @@ export = createStorybookRule({
type: 'suggestion',
fixable: 'code',
hasSuggestions: true,
+ severity: 'error',
docs: {
description: 'Do not use testing-library directly on stories',
categories: [CategoryId.ADDON_INTERACTIONS, CategoryId.RECOMMENDED],
- recommended: 'strict',
},
schema: [],
messages: {
updateImports: 'Update imports',
dontUseTestingLibraryDirectly:
- 'Do not use `{{library}}` directly in the story. You should import the functions from `@storybook/testing-library` instead.',
+ 'Do not use `{{library}}` directly in the story. You should import the functions from `@storybook/test` (preferrably) or `@storybook/testing-library` instead.',
},
},
diff --git a/lib/types/index.ts b/lib/types/index.ts
index 9f772fd..6289305 100644
--- a/lib/types/index.ts
+++ b/lib/types/index.ts
@@ -1,12 +1,8 @@
import { TSESLint } from '@typescript-eslint/utils'
import { CategoryId } from '../utils/constants'
-import {
- RuleRecommendation,
- RuleRecommendationAcrossConfigs,
-} from '@typescript-eslint/utils/ts-eslint'
export type RuleModule = TSESLint.RuleModule<'', []> & {
- meta: { hasSuggestions?: boolean; docs: { recommendedConfig?: 'error' | 'warn' } }
+ meta: { hasSuggestions?: boolean }
}
// These 2 types are copied from @typescript-eslint/experimental-utils' CreateRuleMeta
@@ -20,23 +16,21 @@ export type StorybookRuleMetaDocs = TSESLint.RuleMetaDataDocs & {
* Which configs the rule should be part of
*/
categories?: CategoryId[]
-
- /**
- * If a string config name, which starting config this rule is enabled in.
- * If an object, which settings it has enabled in each of those configs.
- */
- recommended?: RuleRecommendation | RuleRecommendationAcrossConfigs
}
-export type StorybookRuleMeta = TSESLint.RuleMetaData<
+export type StorybookRuleMeta = TSESLint.RuleMetaData<
TMessageIds,
StorybookRuleMetaDocs
->
+> & {
+ /**
+ * Severity of the rule to be defined in eslint config
+ */
+ severity: 'off' | 'warn' | 'error'
+}
// Comment out for testing purposes:
// const docs: StorybookRuleMetaDocs = {
// description: 'bla',
-// recommended: 'strict',
// }
// const meta: StorybookRuleMeta<'someId'> = {
@@ -46,4 +40,5 @@ export type StorybookRuleMeta = TSESLint.RuleMetaDat
// type: 'problem',
// schema: [],
// docs,
+// severity: 'error',
// }
diff --git a/tests/integrations/flat-config.spec.ts b/tests/integrations/flat-config.spec.ts
index 27099da..8a9f43e 100644
--- a/tests/integrations/flat-config.spec.ts
+++ b/tests/integrations/flat-config.spec.ts
@@ -14,7 +14,9 @@ describe('Integration with flat config', () => {
cp.execSync('pnpm i -f', { stdio: 'inherit' })
})
afterEach(() => {
- originalCwd && process.chdir(originalCwd)
+ if (originalCwd) {
+ process.chdir(originalCwd)
+ }
})
it('should work with config', () => {
diff --git a/tests/integrations/legacy-config.spec.ts b/tests/integrations/legacy-config.spec.ts
index 1c1fc32..f441480 100644
--- a/tests/integrations/legacy-config.spec.ts
+++ b/tests/integrations/legacy-config.spec.ts
@@ -14,7 +14,9 @@ describe('Integration with legacy config', () => {
cp.execSync('pnpm i -f', { stdio: 'inherit' })
})
afterEach(() => {
- originalCwd && process.chdir(originalCwd)
+ if (originalCwd) {
+ process.chdir(originalCwd)
+ }
})
it('should work with config', () => {
diff --git a/tools/generate-rule.ts b/tools/generate-rule.ts
index 92b91f5..fe93cbe 100644
--- a/tools/generate-rule.ts
+++ b/tools/generate-rule.ts
@@ -80,11 +80,11 @@ const generateRule = async () => {
defaultOptions: [],
meta: {
type: 'problem', // \`problem\`, \`suggestion\`, or \`layout\`
+ severity: 'error', // or 'warn'
docs: {
description: '${ruleDescription}',
// Add the categories that suit this rule.
categories: [CategoryId.RECOMMENDED],
- recommended: 'recommended',
},
messages: {
anyMessageIdHere: 'Fill me in',
diff --git a/tools/update-lib-flat-configs.ts b/tools/update-lib-flat-configs.ts
index eb99ad8..a1fc2c8 100644
--- a/tools/update-lib-flat-configs.ts
+++ b/tools/update-lib-flat-configs.ts
@@ -28,6 +28,7 @@ function formatCategory(category: TCategory) {
name: 'storybook:${category.categoryId}:setup',
plugins: {
get storybook() {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
return require('../../index')
}
}
diff --git a/tools/utils/rules.ts b/tools/utils/rules.ts
index 86d03ec..6b1c2fa 100644
--- a/tools/utils/rules.ts
+++ b/tools/utils/rules.ts
@@ -2,17 +2,12 @@ import fs from 'fs'
import path from 'path'
import { createStorybookRule } from '../../lib/utils/create-storybook-rule'
+import { StorybookRuleMeta } from '../../lib/types'
const ROOT = path.resolve(__dirname, '../../lib/rules')
export type TRule = ReturnType & {
- meta: {
- docs: {
- categories?: string[]
- category?: string
- excludeFromConfig?: boolean
- }
- }
+ meta: StorybookRuleMeta
}
const rules = fs
@@ -21,20 +16,12 @@ const rules = fs
.map((file) => path.basename(file, '.ts'))
.map((name) => {
const rule = require(path.join(ROOT, name)) as TRule
- const meta = { ...rule.meta }
+ const meta: StorybookRuleMeta = { ...rule.meta }
if (meta.docs && !meta.docs.categories) {
meta.docs = { ...meta.docs }
meta.docs.categories = []
-
- if (meta.docs.category) {
- meta.docs.categories.push(meta.docs.category)
- delete meta.docs.category
- }
-
- if (meta.docs.recommended) {
- meta.docs.categories.push('recommended')
- }
}
+
return {
ruleId: `storybook/${name}`,
name,
@@ -42,7 +29,7 @@ const rules = fs
}
})
// We might have rules which are almost ready but should not be shipped
- .filter((rule) => !rule.meta.docs.excludeFromConfig)
+ .filter((rule) => !rule.meta.docs?.excludeFromConfig)
export type TRules = typeof rules
diff --git a/tools/utils/updates.ts b/tools/utils/updates.ts
index 8d389e0..ec33be6 100644
--- a/tools/utils/updates.ts
+++ b/tools/utils/updates.ts
@@ -15,7 +15,7 @@ export function formatRules(rules: TCategory['rules'], exclude?: string[]) {
const obj = rules.reduce(
(setting, rule) => {
if (!exclude?.includes(rule.ruleId)) {
- setting[rule.ruleId] = rule.meta.docs.recommended || 'error'
+ setting[rule.ruleId] = rule.meta.severity || 'error'
}
return setting
},
@@ -26,7 +26,7 @@ export function formatRules(rules: TCategory['rules'], exclude?: string[]) {
}
export function formatSingleRule(rules: TCategory['rules'], ruleId: string) {
- const ruleOpt = rules.find((rule) => rule.ruleId === ruleId)?.meta.docs.recommended || 'error'
+ const ruleOpt = rules.find((rule) => rule.ruleId === ruleId)?.meta.severity || 'error'
return JSON.stringify({ [ruleId]: ruleOpt }, null, 2)
}