eslint-plugin-storybook 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -35,41 +35,66 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
|
|
|
35
35
|
.split(' ')
|
|
36
36
|
.filter(Boolean)
|
|
37
37
|
.join(' ');
|
|
38
|
-
// any helper functions should go here or else delete this section
|
|
39
38
|
//----------------------------------------------------------------------
|
|
40
39
|
// Public
|
|
41
40
|
//----------------------------------------------------------------------
|
|
42
41
|
return {
|
|
42
|
+
// CSF3
|
|
43
43
|
ExportNamedDeclaration: function (node) {
|
|
44
44
|
// if there are specifiers, node.declaration should be null
|
|
45
45
|
if (!node.declaration)
|
|
46
46
|
return;
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
const decl = node.declaration;
|
|
48
|
+
if ((0, ast_1.isVariableDeclaration)(decl)) {
|
|
49
|
+
const { id, init } = decl.declarations[0];
|
|
50
|
+
if ((0, ast_1.isIdentifier)(id) && (0, ast_1.isObjectExpression)(init)) {
|
|
51
|
+
const storyNameNode = init.properties.find((prop) => {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
return (0, ast_1.isProperty)(prop) &&
|
|
54
|
+
(0, ast_1.isIdentifier)(prop.key) &&
|
|
55
|
+
(((_a = prop.key) === null || _a === void 0 ? void 0 : _a.name) === 'name' || ((_b = prop.key) === null || _b === void 0 ? void 0 : _b.name) === 'storyName');
|
|
56
|
+
});
|
|
57
|
+
if (!storyNameNode) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const { name } = id;
|
|
61
|
+
const resolvedStoryName = resolveStoryName(name);
|
|
62
|
+
//@ts-ignore
|
|
63
|
+
if ((0, ast_1.isLiteral)(storyNameNode.value) && storyNameNode.value.value === resolvedStoryName) {
|
|
64
|
+
context.report({
|
|
65
|
+
node: storyNameNode,
|
|
66
|
+
messageId: 'storyNameIsRedundant',
|
|
67
|
+
suggest: [
|
|
68
|
+
{
|
|
69
|
+
messageId: 'removeRedundantName',
|
|
70
|
+
fix: function (fixer) {
|
|
71
|
+
return fixer.remove(storyNameNode);
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
53
78
|
}
|
|
54
|
-
|
|
55
|
-
|
|
79
|
+
},
|
|
80
|
+
// CSF2
|
|
81
|
+
AssignmentExpression: function (node) {
|
|
82
|
+
if (!(0, ast_1.isExpressionStatement)(node.parent))
|
|
56
83
|
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const resolvedStoryName = resolveStoryName(name);
|
|
64
|
-
if (storyNameNode.value.value === resolvedStoryName) {
|
|
84
|
+
const { left, right } = node;
|
|
85
|
+
if ((0, ast_1.isIdentifier)(left.property) && left.property.name === 'storyName') {
|
|
86
|
+
const propertyName = left.object.name;
|
|
87
|
+
const propertyValue = right.value;
|
|
88
|
+
const resolvedStoryName = resolveStoryName(propertyName);
|
|
89
|
+
if (propertyValue === resolvedStoryName) {
|
|
65
90
|
context.report({
|
|
66
|
-
node:
|
|
91
|
+
node: node,
|
|
67
92
|
messageId: 'storyNameIsRedundant',
|
|
68
93
|
suggest: [
|
|
69
94
|
{
|
|
70
95
|
messageId: 'removeRedundantName',
|
|
71
96
|
fix: function (fixer) {
|
|
72
|
-
return fixer.remove(
|
|
97
|
+
return fixer.remove(node);
|
|
73
98
|
},
|
|
74
99
|
},
|
|
75
100
|
],
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* @author Yann Braga
|
|
5
5
|
*/
|
|
6
6
|
const ast_utils_1 = require("@typescript-eslint/experimental-utils/dist/ast-utils");
|
|
7
|
+
const csf_1 = require("@storybook/csf");
|
|
8
|
+
const utils_1 = require("../utils");
|
|
7
9
|
const ast_1 = require("../utils/ast");
|
|
8
10
|
const constants_1 = require("../utils/constants");
|
|
9
11
|
const create_storybook_rule_1 = require("../utils/create-storybook-rule");
|
|
@@ -43,10 +45,60 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
|
|
|
43
45
|
.replace(new RegExp(/\s/, 'g'), '')
|
|
44
46
|
.replace(new RegExp(/\w/), (s) => s.toUpperCase()));
|
|
45
47
|
};
|
|
48
|
+
const checkAndReportError = (id, nonStoryExportsConfig = {}) => {
|
|
49
|
+
const { name } = id;
|
|
50
|
+
if (!(0, csf_1.isExportStory)(name, nonStoryExportsConfig)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (!isPascalCase(name)) {
|
|
54
|
+
context.report({
|
|
55
|
+
node: id,
|
|
56
|
+
messageId: 'usePascalCase',
|
|
57
|
+
data: {
|
|
58
|
+
name,
|
|
59
|
+
},
|
|
60
|
+
suggest: [
|
|
61
|
+
{
|
|
62
|
+
messageId: 'convertToPascalCase',
|
|
63
|
+
*fix(fixer) {
|
|
64
|
+
var _a;
|
|
65
|
+
const fullText = context.getSourceCode().text;
|
|
66
|
+
const fullName = fullText.slice(id.range[0], id.range[1]);
|
|
67
|
+
const suffix = fullName.substring(name.length);
|
|
68
|
+
const pascal = toPascalCase(name);
|
|
69
|
+
yield fixer.replaceTextRange(id.range, pascal + suffix);
|
|
70
|
+
const scope = context.getScope().childScopes[0];
|
|
71
|
+
if (scope) {
|
|
72
|
+
const variable = (0, ast_utils_1.findVariable)(scope, name);
|
|
73
|
+
for (let i = 0; i < ((_a = variable === null || variable === void 0 ? void 0 : variable.references) === null || _a === void 0 ? void 0 : _a.length); i++) {
|
|
74
|
+
const ref = variable.references[i];
|
|
75
|
+
if (!ref.init) {
|
|
76
|
+
yield fixer.replaceTextRange(ref.identifier.range, pascal);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
46
86
|
//----------------------------------------------------------------------
|
|
47
87
|
// Public
|
|
48
88
|
//----------------------------------------------------------------------
|
|
89
|
+
let meta;
|
|
90
|
+
let nonStoryExportsConfig;
|
|
91
|
+
let namedExports = [];
|
|
49
92
|
return {
|
|
93
|
+
ExportDefaultDeclaration: function (node) {
|
|
94
|
+
meta = (0, utils_1.getMetaObjectExpression)(node, context);
|
|
95
|
+
if (meta) {
|
|
96
|
+
nonStoryExportsConfig = {
|
|
97
|
+
excludeStories: (0, utils_1.getDescriptor)(meta, 'excludeStories'),
|
|
98
|
+
includeStories: (0, utils_1.getDescriptor)(meta, 'includeStories'),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
},
|
|
50
102
|
ExportNamedDeclaration: function (node) {
|
|
51
103
|
// if there are specifiers, node.declaration should be null
|
|
52
104
|
if (!node.declaration)
|
|
@@ -55,38 +107,15 @@ module.exports = (0, create_storybook_rule_1.createStorybookRule)({
|
|
|
55
107
|
if ((0, ast_1.isVariableDeclaration)(decl)) {
|
|
56
108
|
const { id } = decl.declarations[0];
|
|
57
109
|
if ((0, ast_1.isIdentifier)(id)) {
|
|
58
|
-
|
|
59
|
-
if (!isPascalCase(name)) {
|
|
60
|
-
context.report({
|
|
61
|
-
node: id,
|
|
62
|
-
messageId: 'usePascalCase',
|
|
63
|
-
data: {
|
|
64
|
-
name,
|
|
65
|
-
},
|
|
66
|
-
suggest: [
|
|
67
|
-
{
|
|
68
|
-
messageId: 'convertToPascalCase',
|
|
69
|
-
*fix(fixer) {
|
|
70
|
-
const fullText = context.getSourceCode().text;
|
|
71
|
-
const fullName = fullText.slice(id.range[0], id.range[1]);
|
|
72
|
-
const suffix = fullName.substring(name.length);
|
|
73
|
-
const pascal = toPascalCase(name);
|
|
74
|
-
yield fixer.replaceTextRange(id.range, pascal + suffix);
|
|
75
|
-
const variable = (0, ast_utils_1.findVariable)(context.getScope(), name);
|
|
76
|
-
for (let i = 0; i < variable.references.length; i++) {
|
|
77
|
-
const ref = variable.references[i];
|
|
78
|
-
if (!ref.init) {
|
|
79
|
-
yield fixer.replaceTextRange(ref.identifier.range, pascal);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
});
|
|
86
|
-
}
|
|
110
|
+
namedExports.push(id);
|
|
87
111
|
}
|
|
88
112
|
}
|
|
89
113
|
},
|
|
114
|
+
'Program:exit': function () {
|
|
115
|
+
if (namedExports.length) {
|
|
116
|
+
namedExports.forEach((n) => checkAndReportError(n, nonStoryExportsConfig));
|
|
117
|
+
}
|
|
118
|
+
},
|
|
90
119
|
};
|
|
91
120
|
},
|
|
92
121
|
});
|
package/dist/utils/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getMetaObjectExpression = exports.isPlayFunction = exports.docsUrl = void 0;
|
|
3
|
+
exports.getDescriptor = exports.getMetaObjectExpression = exports.isPlayFunction = exports.docsUrl = void 0;
|
|
4
4
|
const ast_utils_1 = require("@typescript-eslint/experimental-utils/dist/ast-utils");
|
|
5
5
|
const ast_1 = require("./ast");
|
|
6
6
|
const docsUrl = (ruleName) => `https://github.com/storybookjs/eslint-plugin-storybook/blob/main/docs/rules/${ruleName}.md`;
|
|
@@ -25,3 +25,25 @@ const getMetaObjectExpression = (node, context) => {
|
|
|
25
25
|
return (0, ast_1.isObjectExpression)(meta) ? meta : null;
|
|
26
26
|
};
|
|
27
27
|
exports.getMetaObjectExpression = getMetaObjectExpression;
|
|
28
|
+
const getDescriptor = (metaDeclaration, propertyName) => {
|
|
29
|
+
const property = metaDeclaration && metaDeclaration.properties.find((p) => p.key && p.key.name === propertyName);
|
|
30
|
+
if (!property) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const { type } = property.value;
|
|
34
|
+
switch (type) {
|
|
35
|
+
case 'ArrayExpression':
|
|
36
|
+
return property.value.elements.map((t) => {
|
|
37
|
+
if (!['StringLiteral', 'Literal'].includes(t.type)) {
|
|
38
|
+
throw new Error(`Unexpected descriptor element: ${t.type}`);
|
|
39
|
+
}
|
|
40
|
+
return t.value;
|
|
41
|
+
});
|
|
42
|
+
case 'Literal':
|
|
43
|
+
case 'RegExpLiteral':
|
|
44
|
+
return property.value.value;
|
|
45
|
+
default:
|
|
46
|
+
throw new Error(`Unexpected descriptor: ${type}`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
exports.getDescriptor = getDescriptor;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-storybook",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Best practice rules for Storybook",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"release": "yarn build && auto shipit"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"@storybook/csf": "^0.0.1",
|
|
48
49
|
"@typescript-eslint/experimental-utils": "^5.3.0",
|
|
49
50
|
"requireindex": "^1.1.0"
|
|
50
51
|
},
|