Skip to content

Commit 0a87433

Browse files
committed
default-exports: Refactor rule to handle meta declaration
1 parent c16dd3a commit 0a87433

File tree

3 files changed

+84
-17
lines changed

3 files changed

+84
-17
lines changed

docs/rules/default-exports.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ export const Primary = {}
1919

2020
Examples of **correct** code for this rule:
2121

22+
```js
23+
const meta = {
24+
title: 'Button',
25+
args: { primary: true },
26+
component: Button,
27+
}
28+
export const meta
29+
30+
export const Primary = {}
31+
```
32+
2233
```js
2334
export default {
2435
title: 'Button',

lib/rules/default-exports.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,9 @@ export = createStorybookRule({
3434
},
3535

3636
create(context) {
37-
// variables should be defined here
38-
3937
//----------------------------------------------------------------------
4038
// Helpers
4139
//----------------------------------------------------------------------
42-
43-
// any helper functions should go here or else delete this section
4440
const getComponentName = (node: TSESTree.Program, filePath: string) => {
4541
const name = path.basename(filePath).split('.')[0]
4642
const imported = node.body.find((stmt: TSESTree.Node) => {
@@ -62,6 +58,7 @@ export = createStorybookRule({
6258
//----------------------------------------------------------------------
6359

6460
let hasDefaultExport = false
61+
let localMetaNode: TSESTree.Node
6562
let hasStoriesOfImport = false
6663

6764
return {
@@ -70,6 +67,17 @@ export = createStorybookRule({
7067
hasStoriesOfImport = true
7168
}
7269
},
70+
VariableDeclaration(node) {
71+
// Take `const meta = {};` into consideration
72+
if (
73+
node.kind === 'const' &&
74+
node.declarations.length === 1 &&
75+
node.declarations[0]?.id.type === 'Identifier' &&
76+
node.declarations[0]?.id.name === 'meta'
77+
) {
78+
localMetaNode = node
79+
}
80+
},
7381
ExportDefaultSpecifier: function () {
7482
hasDefaultExport = true
7583
},
@@ -78,20 +86,28 @@ export = createStorybookRule({
7886
},
7987
'Program:exit': function (program: TSESTree.Program) {
8088
if (!hasDefaultExport && !hasStoriesOfImport) {
81-
const componentName = getComponentName(program, context.getFilename())
89+
const componentName = getComponentName(program, context.filename)
8290
const firstNonImportStatement = program.body.find((n) => !isImportDeclaration(n))
83-
const node = firstNonImportStatement || program.body[0] || program
91+
const node = firstNonImportStatement ?? program.body[0] ?? program
8492

8593
const report = {
8694
node,
8795
messageId: 'shouldHaveDefaultExport',
8896
} as const
8997

9098
const fix: TSESLint.ReportFixFunction = (fixer) => {
91-
const metaDeclaration = componentName
92-
? `export default { component: ${componentName} }\n`
93-
: 'export default {}\n'
94-
return fixer.insertTextBefore(node, metaDeclaration)
99+
const sourceCode = context.sourceCode.getText()
100+
// only add semicolons if needed
101+
const semiCharacter = sourceCode.includes(';') ? ';' : ''
102+
if (localMetaNode) {
103+
const exportStatement = `\nexport default meta${semiCharacter}`
104+
return fixer.insertTextAfter(localMetaNode, exportStatement)
105+
} else {
106+
const exportStatement = componentName
107+
? `export default { component: ${componentName} }${semiCharacter}\n`
108+
: `export default {}${semiCharacter}\n`
109+
return fixer.insertTextBefore(node, exportStatement)
110+
}
95111
}
96112

97113
context.report({

tests/lib/rules/default-exports.test.ts

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ ruleTester.run('default-exports', rule, {
4848
suggestions: [
4949
{
5050
messageId: 'fixSuggestion',
51-
output: 'export default {}\nexport const Primary = () => <button>hello</button>',
51+
output: dedent`
52+
export default {}
53+
export const Primary = () => <button>hello</button>
54+
`,
5255
},
5356
],
5457
},
@@ -70,8 +73,11 @@ ruleTester.run('default-exports', rule, {
7073
suggestions: [
7174
{
7275
messageId: 'fixSuggestion',
73-
output:
74-
"import { MyComponent, Foo } from './MyComponent'\nexport default { component: MyComponent }\nexport const Primary = () => <button>hello</button>",
76+
output: dedent`
77+
import { MyComponent, Foo } from './MyComponent'
78+
export default { component: MyComponent }
79+
export const Primary = () => <button>hello</button>
80+
`,
7581
},
7682
],
7783
},
@@ -93,8 +99,11 @@ ruleTester.run('default-exports', rule, {
9399
suggestions: [
94100
{
95101
messageId: 'fixSuggestion',
96-
output:
97-
"import MyComponent from './MyComponent'\nexport default { component: MyComponent }\nexport const Primary = () => <button>hello</button>",
102+
output: dedent`
103+
import MyComponent from './MyComponent'
104+
export default { component: MyComponent }
105+
export const Primary = () => <button>hello</button>
106+
`,
98107
},
99108
],
100109
},
@@ -116,8 +125,39 @@ ruleTester.run('default-exports', rule, {
116125
suggestions: [
117126
{
118127
messageId: 'fixSuggestion',
119-
output:
120-
"import { MyComponentProps } from './MyComponent'\nexport default {}\nexport const Primary = () => <button>hello</button>",
128+
output: dedent`
129+
import { MyComponentProps } from './MyComponent'
130+
export default {}
131+
export const Primary = () => <button>hello</button>`,
132+
},
133+
],
134+
},
135+
],
136+
},
137+
{
138+
code: dedent`
139+
import { MyComponentProps } from './MyComponent';
140+
export const Primary = () => <button>hello</button>;
141+
const meta = { args: { foo: 'bar' } };
142+
`,
143+
output: dedent`
144+
import { MyComponentProps } from './MyComponent';
145+
export const Primary = () => <button>hello</button>;
146+
const meta = { args: { foo: 'bar' } };
147+
export default meta;
148+
`,
149+
errors: [
150+
{
151+
messageId: 'shouldHaveDefaultExport',
152+
suggestions: [
153+
{
154+
messageId: 'fixSuggestion',
155+
output: dedent`
156+
import { MyComponentProps } from './MyComponent';
157+
export const Primary = () => <button>hello</button>;
158+
const meta = { args: { foo: 'bar' } };
159+
export default meta;
160+
`,
121161
},
122162
],
123163
},

0 commit comments

Comments
 (0)