@depup/eslint-plugin-storybook 10.2.19-depup.0

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.
package/dist/index.js ADDED
@@ -0,0 +1,1451 @@
1
+ import CJS_COMPAT_NODE_URL_bvbg0usgng4 from 'node:url';
2
+ import CJS_COMPAT_NODE_PATH_bvbg0usgng4 from 'node:path';
3
+ import CJS_COMPAT_NODE_MODULE_bvbg0usgng4 from "node:module";
4
+
5
+ var __filename = CJS_COMPAT_NODE_URL_bvbg0usgng4.fileURLToPath(import.meta.url);
6
+ var __dirname = CJS_COMPAT_NODE_PATH_bvbg0usgng4.dirname(__filename);
7
+ var require = CJS_COMPAT_NODE_MODULE_bvbg0usgng4.createRequire(import.meta.url);
8
+
9
+ // ------------------------------------------------------------
10
+ // end of CJS compatibility banner, injected by Storybook's esbuild configuration
11
+ // ------------------------------------------------------------
12
+ var __create = Object.create;
13
+ var __defProp = Object.defineProperty;
14
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
+ var __getOwnPropNames = Object.getOwnPropertyNames;
16
+ var __getProtoOf = Object.getPrototypeOf, __hasOwnProp = Object.prototype.hasOwnProperty;
17
+ var __commonJS = (cb, mod) => function() {
18
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from == "object" || typeof from == "function")
22
+ for (let key of __getOwnPropNames(from))
23
+ !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ return to;
25
+ };
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
27
+ // If the importer is in node compatibility mode or this is not an ESM
28
+ // file that has been converted to a CommonJS file using a Babel-
29
+ // compatible transform (i.e. "__esModule" has not been set), then set
30
+ // "default" to the CommonJS "module.exports" for node compatibility.
31
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target,
32
+ mod
33
+ ));
34
+
35
+ // ../../../node_modules/ts-dedent/dist/index.js
36
+ var require_dist = __commonJS({
37
+ "../../../node_modules/ts-dedent/dist/index.js"(exports) {
38
+ "use strict";
39
+ Object.defineProperty(exports, "__esModule", { value: !0 });
40
+ exports.dedent = void 0;
41
+ function dedent2(templ) {
42
+ for (var values = [], _i = 1; _i < arguments.length; _i++)
43
+ values[_i - 1] = arguments[_i];
44
+ var strings = Array.from(typeof templ == "string" ? [templ] : templ);
45
+ strings[strings.length - 1] = strings[strings.length - 1].replace(/\r?\n([\t ]*)$/, "");
46
+ var indentLengths = strings.reduce(function(arr, str) {
47
+ var matches = str.match(/\n([\t ]+|(?!\s).)/g);
48
+ return matches ? arr.concat(matches.map(function(match) {
49
+ var _a, _b;
50
+ return (_b = (_a = match.match(/[\t ]/g)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
51
+ })) : arr;
52
+ }, []);
53
+ if (indentLengths.length) {
54
+ var pattern_1 = new RegExp(`
55
+ [ ]{` + Math.min.apply(Math, indentLengths) + "}", "g");
56
+ strings = strings.map(function(str) {
57
+ return str.replace(pattern_1, `
58
+ `);
59
+ });
60
+ }
61
+ strings[0] = strings[0].replace(/^\r?\n/, "");
62
+ var string = strings[0];
63
+ return values.forEach(function(value, i) {
64
+ var endentations = string.match(/(?:^|\n)( *)$/), endentation = endentations ? endentations[1] : "", indentedValue = value;
65
+ typeof value == "string" && value.includes(`
66
+ `) && (indentedValue = String(value).split(`
67
+ `).map(function(str, i2) {
68
+ return i2 === 0 ? str : "" + endentation + str;
69
+ }).join(`
70
+ `)), string += indentedValue + strings[i + 1];
71
+ }), string;
72
+ }
73
+ exports.dedent = dedent2;
74
+ exports.default = dedent2;
75
+ }
76
+ });
77
+
78
+ // src/configs/addon-interactions.ts
79
+ var addon_interactions_default = {
80
+ plugins: ["storybook"],
81
+ overrides: [
82
+ {
83
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
84
+ rules: {
85
+ "react-hooks/rules-of-hooks": "off",
86
+ "import-x/no-anonymous-default-export": "off",
87
+ "storybook/await-interactions": "error",
88
+ "storybook/context-in-play-function": "error",
89
+ "storybook/use-storybook-expect": "error",
90
+ "storybook/use-storybook-testing-library": "error"
91
+ }
92
+ },
93
+ {
94
+ files: [".storybook/main.@(js|cjs|mjs|ts)"],
95
+ rules: {
96
+ "storybook/no-uninstalled-addons": "error"
97
+ }
98
+ }
99
+ ]
100
+ };
101
+
102
+ // src/configs/csf.ts
103
+ var csf_default = {
104
+ plugins: ["storybook"],
105
+ overrides: [
106
+ {
107
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
108
+ rules: {
109
+ "react-hooks/rules-of-hooks": "off",
110
+ "import-x/no-anonymous-default-export": "off",
111
+ "storybook/csf-component": "warn",
112
+ "storybook/default-exports": "error",
113
+ "storybook/hierarchy-separator": "warn",
114
+ "storybook/no-redundant-story-name": "warn",
115
+ "storybook/story-exports": "error"
116
+ }
117
+ },
118
+ {
119
+ files: [".storybook/main.@(js|cjs|mjs|ts)"],
120
+ rules: {
121
+ "storybook/no-uninstalled-addons": "error"
122
+ }
123
+ }
124
+ ]
125
+ };
126
+
127
+ // src/configs/csf-strict.ts
128
+ var csf_strict_default = {
129
+ // This file is bundled in an index.js file at the root
130
+ // so the reference is relative to the src directory
131
+ extends: "./configs/csf",
132
+ overrides: [
133
+ {
134
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
135
+ rules: {
136
+ "react-hooks/rules-of-hooks": "off",
137
+ "import-x/no-anonymous-default-export": "off",
138
+ "storybook/no-stories-of": "error",
139
+ "storybook/no-title-property-in-meta": "error"
140
+ }
141
+ }
142
+ ]
143
+ };
144
+
145
+ // src/configs/flat/addon-interactions.ts
146
+ var addon_interactions_default2 = [
147
+ {
148
+ name: "storybook:addon-interactions:setup",
149
+ plugins: {
150
+ get storybook() {
151
+ return index_default;
152
+ }
153
+ }
154
+ },
155
+ {
156
+ name: "storybook:addon-interactions:stories-rules",
157
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
158
+ rules: {
159
+ "react-hooks/rules-of-hooks": "off",
160
+ "import-x/no-anonymous-default-export": "off",
161
+ "storybook/await-interactions": "error",
162
+ "storybook/context-in-play-function": "error",
163
+ "storybook/use-storybook-expect": "error",
164
+ "storybook/use-storybook-testing-library": "error"
165
+ }
166
+ },
167
+ {
168
+ name: "storybook:addon-interactions:main-rules",
169
+ files: [".storybook/main.@(js|cjs|mjs|ts)"],
170
+ rules: {
171
+ "storybook/no-uninstalled-addons": "error"
172
+ }
173
+ }
174
+ ];
175
+
176
+ // src/configs/flat/csf.ts
177
+ var csf_default2 = [
178
+ {
179
+ name: "storybook:csf:setup",
180
+ plugins: {
181
+ get storybook() {
182
+ return index_default;
183
+ }
184
+ }
185
+ },
186
+ {
187
+ name: "storybook:csf:stories-rules",
188
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
189
+ rules: {
190
+ "react-hooks/rules-of-hooks": "off",
191
+ "import-x/no-anonymous-default-export": "off",
192
+ "storybook/csf-component": "warn",
193
+ "storybook/default-exports": "error",
194
+ "storybook/hierarchy-separator": "warn",
195
+ "storybook/no-redundant-story-name": "warn",
196
+ "storybook/story-exports": "error"
197
+ }
198
+ },
199
+ {
200
+ name: "storybook:csf:main-rules",
201
+ files: [".storybook/main.@(js|cjs|mjs|ts)"],
202
+ rules: {
203
+ "storybook/no-uninstalled-addons": "error"
204
+ }
205
+ }
206
+ ];
207
+
208
+ // src/configs/flat/csf-strict.ts
209
+ var csf_strict_default2 = [
210
+ ...csf_default2,
211
+ {
212
+ name: "storybook:csf-strict:rules",
213
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
214
+ rules: {
215
+ "react-hooks/rules-of-hooks": "off",
216
+ "import-x/no-anonymous-default-export": "off",
217
+ "storybook/no-stories-of": "error",
218
+ "storybook/no-title-property-in-meta": "error"
219
+ }
220
+ }
221
+ ];
222
+
223
+ // src/configs/flat/recommended.ts
224
+ var recommended_default = [
225
+ {
226
+ name: "storybook:recommended:setup",
227
+ plugins: {
228
+ get storybook() {
229
+ return index_default;
230
+ }
231
+ }
232
+ },
233
+ {
234
+ name: "storybook:recommended:stories-rules",
235
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
236
+ rules: {
237
+ "react-hooks/rules-of-hooks": "off",
238
+ "import-x/no-anonymous-default-export": "off",
239
+ "storybook/await-interactions": "error",
240
+ "storybook/context-in-play-function": "error",
241
+ "storybook/default-exports": "error",
242
+ "storybook/hierarchy-separator": "warn",
243
+ "storybook/no-redundant-story-name": "warn",
244
+ "storybook/no-renderer-packages": "error",
245
+ "storybook/prefer-pascal-case": "warn",
246
+ "storybook/story-exports": "error",
247
+ "storybook/use-storybook-expect": "error",
248
+ "storybook/use-storybook-testing-library": "error"
249
+ }
250
+ },
251
+ {
252
+ name: "storybook:recommended:main-rules",
253
+ files: [".storybook/main.@(js|cjs|mjs|ts)"],
254
+ rules: {
255
+ "storybook/no-uninstalled-addons": "error"
256
+ }
257
+ }
258
+ ];
259
+
260
+ // src/configs/recommended.ts
261
+ var recommended_default2 = {
262
+ plugins: ["storybook"],
263
+ overrides: [
264
+ {
265
+ files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"],
266
+ rules: {
267
+ "react-hooks/rules-of-hooks": "off",
268
+ "import-x/no-anonymous-default-export": "off",
269
+ "storybook/await-interactions": "error",
270
+ "storybook/context-in-play-function": "error",
271
+ "storybook/default-exports": "error",
272
+ "storybook/hierarchy-separator": "warn",
273
+ "storybook/no-redundant-story-name": "warn",
274
+ "storybook/no-renderer-packages": "error",
275
+ "storybook/prefer-pascal-case": "warn",
276
+ "storybook/story-exports": "error",
277
+ "storybook/use-storybook-expect": "error",
278
+ "storybook/use-storybook-testing-library": "error"
279
+ }
280
+ },
281
+ {
282
+ files: [".storybook/main.@(js|cjs|mjs|ts)"],
283
+ rules: {
284
+ "storybook/no-uninstalled-addons": "error"
285
+ }
286
+ }
287
+ ]
288
+ };
289
+
290
+ // src/utils/ast.ts
291
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
292
+ import { ASTUtils } from "@typescript-eslint/utils";
293
+ var isNodeOfType = (nodeType) => (node) => node?.type === nodeType, isAwaitExpression = isNodeOfType(AST_NODE_TYPES.AwaitExpression), isIdentifier = isNodeOfType(AST_NODE_TYPES.Identifier), isVariableDeclarator = isNodeOfType(AST_NODE_TYPES.VariableDeclarator), isArrayExpression = isNodeOfType(AST_NODE_TYPES.ArrayExpression), isArrowFunctionExpression = isNodeOfType(AST_NODE_TYPES.ArrowFunctionExpression), isBlockStatement = isNodeOfType(AST_NODE_TYPES.BlockStatement), isCallExpression = isNodeOfType(AST_NODE_TYPES.CallExpression), isExpressionStatement = isNodeOfType(AST_NODE_TYPES.ExpressionStatement), isVariableDeclaration = isNodeOfType(AST_NODE_TYPES.VariableDeclaration), isAssignmentExpression = isNodeOfType(AST_NODE_TYPES.AssignmentExpression), isSequenceExpression = isNodeOfType(AST_NODE_TYPES.SequenceExpression), isImportDeclaration = isNodeOfType(AST_NODE_TYPES.ImportDeclaration), isImportDefaultSpecifier = isNodeOfType(AST_NODE_TYPES.ImportDefaultSpecifier), isImportNamespaceSpecifier = isNodeOfType(AST_NODE_TYPES.ImportNamespaceSpecifier), isImportSpecifier = isNodeOfType(AST_NODE_TYPES.ImportSpecifier), isJSXAttribute = isNodeOfType(AST_NODE_TYPES.JSXAttribute), isLiteral = isNodeOfType(AST_NODE_TYPES.Literal), isMemberExpression = isNodeOfType(AST_NODE_TYPES.MemberExpression), isNewExpression = isNodeOfType(AST_NODE_TYPES.NewExpression), isObjectExpression = isNodeOfType(AST_NODE_TYPES.ObjectExpression), isObjectPattern = isNodeOfType(AST_NODE_TYPES.ObjectPattern), isProperty = isNodeOfType(AST_NODE_TYPES.Property), isSpreadElement = isNodeOfType(AST_NODE_TYPES.SpreadElement), isRestElement = isNodeOfType(AST_NODE_TYPES.RestElement), isReturnStatement = isNodeOfType(AST_NODE_TYPES.ReturnStatement), isFunctionDeclaration = isNodeOfType(AST_NODE_TYPES.FunctionDeclaration), isFunctionExpression = isNodeOfType(AST_NODE_TYPES.FunctionExpression), isProgram = isNodeOfType(AST_NODE_TYPES.Program), isTSTypeAliasDeclaration = isNodeOfType(AST_NODE_TYPES.TSTypeAliasDeclaration), isTSInterfaceDeclaration = isNodeOfType(AST_NODE_TYPES.TSInterfaceDeclaration), isTSAsExpression = isNodeOfType(AST_NODE_TYPES.TSAsExpression), isTSSatisfiesExpression = isNodeOfType(AST_NODE_TYPES.TSSatisfiesExpression), isTSNonNullExpression = isNodeOfType(AST_NODE_TYPES.TSNonNullExpression), isMetaProperty = isNodeOfType(AST_NODE_TYPES.MetaProperty);
294
+
295
+ // src/utils/create-storybook-rule.ts
296
+ import { ESLintUtils } from "@typescript-eslint/utils";
297
+
298
+ // src/utils/index.ts
299
+ import { isExportStory } from "storybook/internal/csf";
300
+ import { ASTUtils as ASTUtils2 } from "@typescript-eslint/utils";
301
+ var docsUrl = (ruleName) => `https://github.com/storybookjs/storybook/blob/next/code/lib/eslint-plugin/docs/rules/${ruleName}.md`, getMetaObjectExpression = (node, context) => {
302
+ let meta = node.declaration, { sourceCode } = context;
303
+ if (isIdentifier(meta)) {
304
+ let scope = sourceCode.getScope ? sourceCode.getScope(node) : context.getScope(), variable = ASTUtils2.findVariable(scope, meta.name), decl = variable && variable.defs.find((def) => isVariableDeclarator(def.node));
305
+ decl && isVariableDeclarator(decl.node) && (meta = decl.node.init);
306
+ }
307
+ return (isTSAsExpression(meta) || isTSSatisfiesExpression(meta)) && (meta = meta.expression), isObjectExpression(meta) ? meta : null;
308
+ }, getDescriptor = (metaDeclaration, propertyName) => {
309
+ let property = metaDeclaration && metaDeclaration.properties.find(
310
+ (p) => "key" in p && "name" in p.key && p.key.name === propertyName
311
+ );
312
+ if (!property || isSpreadElement(property))
313
+ return;
314
+ let { type } = property.value;
315
+ switch (type) {
316
+ case "ArrayExpression":
317
+ return property.value.elements.map((t) => {
318
+ if (t === null)
319
+ throw new Error("Unexpected descriptor element: null");
320
+ if (!["StringLiteral", "Literal"].includes(t.type))
321
+ throw new Error(`Unexpected descriptor element: ${t.type}`);
322
+ return t.value;
323
+ });
324
+ case "Literal":
325
+ // @ts-expect-error TODO: Investigation needed. Type systems says, that "RegExpLiteral" does not exist
326
+ case "RegExpLiteral":
327
+ return property.value.value;
328
+ default:
329
+ throw new Error(`Unexpected descriptor: ${type}`);
330
+ }
331
+ }, isValidStoryExport = (node, nonStoryExportsConfig) => isExportStory(node.name, nonStoryExportsConfig) && node.name !== "__namedExportsOrder", getAllNamedExports = (node) => {
332
+ if (!node.declaration && node.specifiers)
333
+ return node.specifiers.reduce((acc, specifier) => (isIdentifier(specifier.exported) && acc.push(specifier.exported), acc), []);
334
+ let decl = node.declaration;
335
+ if (isVariableDeclaration(decl)) {
336
+ let declaration = decl.declarations[0];
337
+ if (declaration) {
338
+ let { id } = declaration;
339
+ if (isIdentifier(id))
340
+ return [id];
341
+ }
342
+ }
343
+ return isFunctionDeclaration(decl) && isIdentifier(decl.id) ? [decl.id] : [];
344
+ };
345
+
346
+ // src/utils/create-storybook-rule.ts
347
+ function createStorybookRule({
348
+ create,
349
+ meta,
350
+ ...remainingConfig
351
+ }) {
352
+ return ESLintUtils.RuleCreator(docsUrl)({
353
+ ...remainingConfig,
354
+ create,
355
+ meta: {
356
+ ...meta,
357
+ docs: {
358
+ ...meta.docs
359
+ },
360
+ defaultOptions: remainingConfig.defaultOptions
361
+ }
362
+ });
363
+ }
364
+
365
+ // src/rules/await-interactions.ts
366
+ var await_interactions_default = createStorybookRule({
367
+ name: "await-interactions",
368
+ defaultOptions: [],
369
+ meta: {
370
+ severity: "error",
371
+ docs: {
372
+ description: "Interactions should be awaited",
373
+ categories: ["addon-interactions" /* ADDON_INTERACTIONS */, "recommended" /* RECOMMENDED */]
374
+ },
375
+ messages: {
376
+ interactionShouldBeAwaited: "Interaction should be awaited: {{method}}",
377
+ fixSuggestion: "Add `await` to method"
378
+ },
379
+ type: "problem",
380
+ fixable: "code",
381
+ hasSuggestions: !0,
382
+ schema: []
383
+ },
384
+ create(context) {
385
+ let FUNCTIONS_TO_BE_AWAITED = [
386
+ "waitFor",
387
+ "waitForElementToBeRemoved",
388
+ "wait",
389
+ "waitForElement",
390
+ "waitForDomChange",
391
+ "userEvent",
392
+ "play"
393
+ ], getMethodThatShouldBeAwaited = (expr) => {
394
+ let shouldAwait = (name) => FUNCTIONS_TO_BE_AWAITED.includes(name) || name.startsWith("findBy");
395
+ return isArrowFunctionExpression(expr.parent) || isReturnStatement(expr.parent) ? null : isMemberExpression(expr.callee) && isIdentifier(expr.callee.object) && shouldAwait(expr.callee.object.name) ? expr.callee.object : isTSNonNullExpression(expr.callee) && isMemberExpression(expr.callee.expression) && isIdentifier(expr.callee.expression.property) && shouldAwait(expr.callee.expression.property.name) ? expr.callee.expression.property : isMemberExpression(expr.callee) && isIdentifier(expr.callee.property) && shouldAwait(expr.callee.property.name) || isMemberExpression(expr.callee) && isCallExpression(expr.callee.object) && isIdentifier(expr.callee.object.callee) && isIdentifier(expr.callee.property) && expr.callee.object.callee.name === "expect" ? expr.callee.property : isIdentifier(expr.callee) && shouldAwait(expr.callee.name) ? expr.callee : null;
396
+ }, getClosestFunctionAncestor = (node) => {
397
+ let parent = node.parent;
398
+ if (!(!parent || isProgram(parent)))
399
+ return isArrowFunctionExpression(parent) || isFunctionExpression(parent) || isFunctionDeclaration(parent) ? node.parent : getClosestFunctionAncestor(parent);
400
+ }, isUserEventFromStorybookImported = (node) => (node.source.value === "@storybook/testing-library" || node.source.value === "@storybook/test") && node.specifiers.find(
401
+ (spec) => isImportSpecifier(spec) && "name" in spec.imported && spec.imported.name === "userEvent" && spec.local.name === "userEvent"
402
+ ) !== void 0, isExpectFromStorybookImported = (node) => (node.source.value === "@storybook/jest" || node.source.value === "@storybook/test") && node.specifiers.find(
403
+ (spec) => isImportSpecifier(spec) && "name" in spec.imported && spec.imported.name === "expect"
404
+ ) !== void 0, isImportedFromStorybook = !0, invocationsThatShouldBeAwaited = [];
405
+ return {
406
+ ImportDeclaration(node) {
407
+ isImportedFromStorybook = isUserEventFromStorybookImported(node) || isExpectFromStorybookImported(node);
408
+ },
409
+ VariableDeclarator(node) {
410
+ isImportedFromStorybook = isImportedFromStorybook && isIdentifier(node.id) && node.id.name !== "userEvent";
411
+ },
412
+ CallExpression(node) {
413
+ let method = getMethodThatShouldBeAwaited(node);
414
+ method && !isAwaitExpression(node.parent) && !isAwaitExpression(node.parent?.parent) && invocationsThatShouldBeAwaited.push({ node, method });
415
+ },
416
+ "Program:exit": function() {
417
+ isImportedFromStorybook && invocationsThatShouldBeAwaited.length && invocationsThatShouldBeAwaited.forEach(({ node, method }) => {
418
+ let parentFnNode = getClosestFunctionAncestor(node), parentFnNeedsAsync = parentFnNode && !("async" in parentFnNode && parentFnNode.async), fixFn = (fixer) => {
419
+ let fixerResult = [fixer.insertTextBefore(node, "await ")];
420
+ return parentFnNeedsAsync && fixerResult.push(fixer.insertTextBefore(parentFnNode, "async ")), fixerResult;
421
+ };
422
+ context.report({
423
+ node,
424
+ messageId: "interactionShouldBeAwaited",
425
+ data: {
426
+ method: method.name
427
+ },
428
+ fix: fixFn,
429
+ suggest: [
430
+ {
431
+ messageId: "fixSuggestion",
432
+ fix: fixFn
433
+ }
434
+ ]
435
+ });
436
+ });
437
+ }
438
+ };
439
+ }
440
+ });
441
+
442
+ // src/rules/context-in-play-function.ts
443
+ var context_in_play_function_default = createStorybookRule({
444
+ name: "context-in-play-function",
445
+ defaultOptions: [],
446
+ meta: {
447
+ type: "problem",
448
+ severity: "error",
449
+ docs: {
450
+ description: "Pass a context when invoking play function of another story",
451
+ categories: ["recommended" /* RECOMMENDED */, "addon-interactions" /* ADDON_INTERACTIONS */]
452
+ },
453
+ messages: {
454
+ passContextToPlayFunction: "Pass a context when invoking play function of another story"
455
+ },
456
+ fixable: void 0,
457
+ schema: []
458
+ },
459
+ create(context) {
460
+ let isPlayFunctionFromAnotherStory = (expr) => !!(isTSNonNullExpression(expr.callee) && isMemberExpression(expr.callee.expression) && isIdentifier(expr.callee.expression.property) && expr.callee.expression.property.name === "play" || isMemberExpression(expr.callee) && isIdentifier(expr.callee.property) && expr.callee.property.name === "play"), getParentParameterName = (node) => {
461
+ if (!isArrowFunctionExpression(node))
462
+ return node.parent ? getParentParameterName(node.parent) : void 0;
463
+ if (node.params.length !== 0 && node.params.length >= 1) {
464
+ let param = node.params[0];
465
+ if (isIdentifier(param))
466
+ return param.name;
467
+ if (isObjectPattern(param)) {
468
+ if (param.properties.find((prop) => prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === "context"))
469
+ return "context";
470
+ let restElement = param.properties.find(isRestElement);
471
+ return !restElement || !isIdentifier(restElement.argument) ? void 0 : restElement.argument.name;
472
+ }
473
+ }
474
+ }, isNotPassingContextCorrectly = (expr) => {
475
+ let firstExpressionArgument = expr.arguments[0];
476
+ if (!firstExpressionArgument)
477
+ return !0;
478
+ let contextVariableName = getParentParameterName(expr);
479
+ return contextVariableName ? !(expr.arguments.length === 1 && isIdentifier(firstExpressionArgument) && firstExpressionArgument.name === contextVariableName || isObjectExpression(firstExpressionArgument) && firstExpressionArgument.properties.some((prop) => isSpreadElement(prop) && isIdentifier(prop.argument) && prop.argument.name === contextVariableName)) : !0;
480
+ }, invocationsWithoutProperContext = [];
481
+ return {
482
+ CallExpression(node) {
483
+ isPlayFunctionFromAnotherStory(node) && isNotPassingContextCorrectly(node) && invocationsWithoutProperContext.push(node);
484
+ },
485
+ "Program:exit": function() {
486
+ invocationsWithoutProperContext.forEach((node) => {
487
+ context.report({
488
+ node,
489
+ messageId: "passContextToPlayFunction"
490
+ });
491
+ });
492
+ }
493
+ };
494
+ }
495
+ });
496
+
497
+ // src/rules/csf-component.ts
498
+ var csf_component_default = createStorybookRule({
499
+ name: "csf-component",
500
+ defaultOptions: [],
501
+ meta: {
502
+ type: "suggestion",
503
+ severity: "warn",
504
+ docs: {
505
+ description: "The component property should be set",
506
+ categories: ["csf" /* CSF */]
507
+ },
508
+ messages: {
509
+ missingComponentProperty: "Missing component property."
510
+ },
511
+ schema: []
512
+ },
513
+ create(context) {
514
+ return {
515
+ ExportDefaultDeclaration(node) {
516
+ let meta = getMetaObjectExpression(node, context);
517
+ if (!meta)
518
+ return null;
519
+ meta.properties.find(
520
+ (property) => !isSpreadElement(property) && "name" in property.key && property.key.name === "component"
521
+ ) || context.report({
522
+ node,
523
+ messageId: "missingComponentProperty"
524
+ });
525
+ }
526
+ };
527
+ }
528
+ });
529
+
530
+ // src/rules/default-exports.ts
531
+ import path from "path";
532
+ var default_exports_default = createStorybookRule({
533
+ name: "default-exports",
534
+ defaultOptions: [],
535
+ meta: {
536
+ type: "problem",
537
+ severity: "error",
538
+ docs: {
539
+ description: "Story files should have a default export",
540
+ categories: ["csf" /* CSF */, "recommended" /* RECOMMENDED */]
541
+ },
542
+ messages: {
543
+ shouldHaveDefaultExport: "The file should have a default export.",
544
+ fixSuggestion: "Add default export"
545
+ },
546
+ fixable: "code",
547
+ hasSuggestions: !0,
548
+ schema: []
549
+ },
550
+ create(context) {
551
+ let getComponentName = (node, filePath) => {
552
+ let name = path.basename(filePath).split(".")[0];
553
+ return node.body.find((stmt) => {
554
+ if (isImportDeclaration(stmt) && isLiteral(stmt.source) && stmt.source.value.startsWith(`./${name}`))
555
+ return !!stmt.specifiers.find(
556
+ (spec) => isIdentifier(spec.local) && spec.local.name === name
557
+ );
558
+ }) ? name : null;
559
+ }, hasDefaultExport = !1, isCsf4Style = !1, hasStoriesOfImport = !1;
560
+ return {
561
+ ImportSpecifier(node) {
562
+ "name" in node.imported && node.imported.name === "storiesOf" && (hasStoriesOfImport = !0);
563
+ },
564
+ VariableDeclaration(node) {
565
+ node.parent.type === "Program" && node.declarations.forEach((declaration) => {
566
+ let init = declaration.init;
567
+ if (init && init.type === "CallExpression") {
568
+ let callee = init.callee;
569
+ callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.name === "meta" && (isCsf4Style = !0);
570
+ }
571
+ });
572
+ },
573
+ ExportDefaultSpecifier: function() {
574
+ hasDefaultExport = !0;
575
+ },
576
+ ExportDefaultDeclaration: function() {
577
+ hasDefaultExport = !0;
578
+ },
579
+ "Program:exit": function(program) {
580
+ if (!isCsf4Style && !hasDefaultExport && !hasStoriesOfImport) {
581
+ let componentName = getComponentName(program, context.filename), node = program.body.find((n) => !isImportDeclaration(n)) || program.body[0] || program, report = {
582
+ node,
583
+ messageId: "shouldHaveDefaultExport"
584
+ }, fix = (fixer) => {
585
+ let metaDeclaration = componentName ? `export default { component: ${componentName} }
586
+ ` : `export default {}
587
+ `;
588
+ return fixer.insertTextBefore(node, metaDeclaration);
589
+ };
590
+ context.report({
591
+ ...report,
592
+ fix,
593
+ suggest: [
594
+ {
595
+ messageId: "fixSuggestion",
596
+ fix
597
+ }
598
+ ]
599
+ });
600
+ }
601
+ }
602
+ };
603
+ }
604
+ });
605
+
606
+ // src/rules/hierarchy-separator.ts
607
+ var hierarchy_separator_default = createStorybookRule({
608
+ name: "hierarchy-separator",
609
+ defaultOptions: [],
610
+ meta: {
611
+ type: "problem",
612
+ fixable: "code",
613
+ hasSuggestions: !0,
614
+ severity: "warn",
615
+ docs: {
616
+ description: "Deprecated hierarchy separator in title property",
617
+ categories: ["csf" /* CSF */, "recommended" /* RECOMMENDED */]
618
+ },
619
+ messages: {
620
+ useCorrectSeparators: "Use correct separators",
621
+ deprecatedHierarchySeparator: "Deprecated hierarchy separator in title property: {{metaTitle}}."
622
+ },
623
+ schema: []
624
+ },
625
+ create: function(context) {
626
+ return {
627
+ ExportDefaultDeclaration: function(node) {
628
+ let meta = getMetaObjectExpression(node, context);
629
+ if (!meta)
630
+ return null;
631
+ let titleNode = meta.properties.find(
632
+ (prop) => !isSpreadElement(prop) && "name" in prop.key && prop.key?.name === "title"
633
+ );
634
+ if (!titleNode || !isLiteral(titleNode.value))
635
+ return;
636
+ let metaTitle = titleNode.value.raw || "";
637
+ metaTitle.includes("|") && context.report({
638
+ node: titleNode,
639
+ messageId: "deprecatedHierarchySeparator",
640
+ data: { metaTitle },
641
+ // In case we want this to be auto fixed by --fix
642
+ fix: function(fixer) {
643
+ return fixer.replaceTextRange(titleNode.value.range, metaTitle.replace(/\|/g, "/"));
644
+ },
645
+ suggest: [
646
+ {
647
+ messageId: "useCorrectSeparators",
648
+ fix: function(fixer) {
649
+ return fixer.replaceTextRange(
650
+ titleNode.value.range,
651
+ metaTitle.replace(/\|/g, "/")
652
+ );
653
+ }
654
+ }
655
+ ]
656
+ });
657
+ }
658
+ };
659
+ }
660
+ });
661
+
662
+ // src/rules/meta-inline-properties.ts
663
+ var meta_inline_properties_default = createStorybookRule({
664
+ name: "meta-inline-properties",
665
+ defaultOptions: [{ csfVersion: 3 }],
666
+ meta: {
667
+ type: "problem",
668
+ severity: "error",
669
+ docs: {
670
+ description: "Meta should only have inline properties",
671
+ categories: ["csf" /* CSF */, "recommended" /* RECOMMENDED */],
672
+ excludeFromConfig: !0
673
+ },
674
+ messages: {
675
+ metaShouldHaveInlineProperties: "Meta should only have inline properties: {{property}}"
676
+ },
677
+ schema: [
678
+ {
679
+ type: "object",
680
+ properties: {
681
+ csfVersion: {
682
+ type: "number"
683
+ }
684
+ },
685
+ additionalProperties: !1
686
+ }
687
+ ]
688
+ },
689
+ create(context) {
690
+ let isInline = (node) => node && typeof node == "object" && "value" in node ? node.value.type === "ObjectExpression" || node.value.type === "Literal" || node.value.type === "ArrayExpression" : !1;
691
+ return {
692
+ ExportDefaultDeclaration(node) {
693
+ let meta = getMetaObjectExpression(node, context);
694
+ if (!meta)
695
+ return null;
696
+ let ruleProperties = ["title", "args"], dynamicProperties = [];
697
+ meta.properties.filter(
698
+ (prop) => "key" in prop && "name" in prop.key && ruleProperties.includes(prop.key.name)
699
+ ).forEach((metaNode) => {
700
+ isInline(metaNode) || dynamicProperties.push(metaNode);
701
+ }), dynamicProperties.length > 0 && dynamicProperties.forEach((propertyNode) => {
702
+ context.report({
703
+ node: propertyNode,
704
+ messageId: "metaShouldHaveInlineProperties",
705
+ data: {
706
+ property: propertyNode.key?.name
707
+ }
708
+ });
709
+ });
710
+ }
711
+ };
712
+ }
713
+ });
714
+
715
+ // src/rules/meta-satisfies-type.ts
716
+ import { ASTUtils as ASTUtils3, AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/utils";
717
+ var meta_satisfies_type_default = createStorybookRule({
718
+ name: "meta-satisfies-type",
719
+ defaultOptions: [],
720
+ meta: {
721
+ type: "problem",
722
+ fixable: "code",
723
+ severity: "error",
724
+ docs: {
725
+ description: "Meta should use `satisfies Meta`",
726
+ categories: [],
727
+ excludeFromConfig: !0
728
+ },
729
+ messages: {
730
+ metaShouldSatisfyType: "CSF Meta should use `satisfies` for type safety"
731
+ },
732
+ schema: []
733
+ },
734
+ create(context) {
735
+ let sourceCode = context.sourceCode, getTextWithParentheses = (node) => {
736
+ let beforeCount = 0, afterCount = 0;
737
+ if (ASTUtils3.isParenthesized(node, sourceCode)) {
738
+ let bodyOpeningParen = sourceCode.getTokenBefore(node, ASTUtils3.isOpeningParenToken), bodyClosingParen = sourceCode.getTokenAfter(node, ASTUtils3.isClosingParenToken);
739
+ bodyOpeningParen && bodyClosingParen && (beforeCount = node.range[0] - bodyOpeningParen.range[0], afterCount = bodyClosingParen.range[1] - node.range[1]);
740
+ }
741
+ return sourceCode.getText(node, beforeCount, afterCount);
742
+ }, getFixer = (meta) => {
743
+ let { parent } = meta;
744
+ if (parent)
745
+ switch (parent.type) {
746
+ // {} as Meta
747
+ case AST_NODE_TYPES2.TSAsExpression:
748
+ return (fixer) => [
749
+ fixer.replaceText(parent, getTextWithParentheses(meta)),
750
+ fixer.insertTextAfter(
751
+ parent,
752
+ ` satisfies ${getTextWithParentheses(parent.typeAnnotation)}`
753
+ )
754
+ ];
755
+ // const meta: Meta = {}
756
+ case AST_NODE_TYPES2.VariableDeclarator: {
757
+ let { typeAnnotation } = parent.id;
758
+ return typeAnnotation ? (fixer) => [
759
+ fixer.remove(typeAnnotation),
760
+ fixer.insertTextAfter(
761
+ meta,
762
+ ` satisfies ${getTextWithParentheses(typeAnnotation.typeAnnotation)}`
763
+ )
764
+ ] : void 0;
765
+ }
766
+ default:
767
+ return;
768
+ }
769
+ };
770
+ return {
771
+ ExportDefaultDeclaration(node) {
772
+ let meta = getMetaObjectExpression(node, context);
773
+ if (!meta)
774
+ return null;
775
+ (!meta.parent || !isTSSatisfiesExpression(meta.parent)) && context.report({
776
+ node: meta,
777
+ messageId: "metaShouldSatisfyType",
778
+ fix: getFixer(meta)
779
+ });
780
+ }
781
+ };
782
+ }
783
+ });
784
+
785
+ // src/rules/no-redundant-story-name.ts
786
+ import { storyNameFromExport } from "storybook/internal/csf";
787
+ var no_redundant_story_name_default = createStorybookRule({
788
+ name: "no-redundant-story-name",
789
+ defaultOptions: [],
790
+ meta: {
791
+ type: "suggestion",
792
+ fixable: "code",
793
+ hasSuggestions: !0,
794
+ severity: "warn",
795
+ docs: {
796
+ description: "A story should not have a redundant name property",
797
+ categories: ["csf" /* CSF */, "recommended" /* RECOMMENDED */]
798
+ },
799
+ messages: {
800
+ removeRedundantName: "Remove redundant name",
801
+ storyNameIsRedundant: "Named exports should not use the name annotation if it is redundant to the name that would be generated by the export name"
802
+ },
803
+ schema: []
804
+ },
805
+ create(context) {
806
+ return {
807
+ // CSF3
808
+ ExportNamedDeclaration: function(node) {
809
+ if (!node.declaration)
810
+ return;
811
+ let decl = node.declaration;
812
+ if (isVariableDeclaration(decl)) {
813
+ let declaration = decl.declarations[0];
814
+ if (declaration == null)
815
+ return;
816
+ let { id, init } = declaration;
817
+ if (isIdentifier(id) && isObjectExpression(init)) {
818
+ let storyNameNode = init.properties.find(
819
+ (prop) => isProperty(prop) && isIdentifier(prop.key) && (prop.key?.name === "name" || prop.key?.name === "storyName")
820
+ );
821
+ if (!storyNameNode)
822
+ return;
823
+ let { name } = id, resolvedStoryName = storyNameFromExport(name);
824
+ !isSpreadElement(storyNameNode) && isLiteral(storyNameNode.value) && storyNameNode.value.value === resolvedStoryName && context.report({
825
+ node: storyNameNode,
826
+ messageId: "storyNameIsRedundant",
827
+ suggest: [
828
+ {
829
+ messageId: "removeRedundantName",
830
+ fix: function(fixer) {
831
+ return fixer.remove(storyNameNode);
832
+ }
833
+ }
834
+ ]
835
+ });
836
+ }
837
+ }
838
+ },
839
+ // CSF2
840
+ AssignmentExpression: function(node) {
841
+ if (!isExpressionStatement(node.parent))
842
+ return;
843
+ let { left, right } = node;
844
+ if ("property" in left && isIdentifier(left.property) && !isMetaProperty(left) && left.property.name === "storyName") {
845
+ if (!("name" in left.object && "value" in right))
846
+ return;
847
+ let propertyName = left.object.name, propertyValue = right.value, resolvedStoryName = storyNameFromExport(propertyName);
848
+ propertyValue === resolvedStoryName && context.report({
849
+ node,
850
+ messageId: "storyNameIsRedundant",
851
+ suggest: [
852
+ {
853
+ messageId: "removeRedundantName",
854
+ fix: function(fixer) {
855
+ return fixer.remove(node);
856
+ }
857
+ }
858
+ ]
859
+ });
860
+ }
861
+ }
862
+ };
863
+ }
864
+ });
865
+
866
+ // src/rules/no-renderer-packages.ts
867
+ var rendererToFrameworks = {
868
+ "@storybook/html": ["@storybook/html-vite", "@storybook/html-webpack5"],
869
+ "@storybook/preact": ["@storybook/preact-vite", "@storybook/preact-webpack5"],
870
+ "@storybook/react": [
871
+ "@storybook/nextjs",
872
+ "@storybook/react-vite",
873
+ "@storybook/nextjs-vite",
874
+ "@storybook/react-webpack5",
875
+ "@storybook/react-native-web-vite"
876
+ ],
877
+ "@storybook/server": ["@storybook/server-webpack5"],
878
+ "@storybook/svelte": [
879
+ "@storybook/svelte-vite",
880
+ "@storybook/svelte-webpack5",
881
+ "@storybook/sveltekit"
882
+ ],
883
+ "@storybook/vue3": ["@storybook/vue3-vite", "@storybook/vue3-webpack5"],
884
+ "@storybook/web-components": [
885
+ "@storybook/web-components-vite",
886
+ "@storybook/web-components-webpack5"
887
+ ]
888
+ }, no_renderer_packages_default = createStorybookRule({
889
+ name: "no-renderer-packages",
890
+ defaultOptions: [],
891
+ meta: {
892
+ type: "problem",
893
+ severity: "error",
894
+ docs: {
895
+ description: "Do not import renderer packages directly in stories",
896
+ categories: ["recommended" /* RECOMMENDED */]
897
+ },
898
+ schema: [],
899
+ messages: {
900
+ noRendererPackages: 'Do not import renderer package "{{rendererPackage}}" directly. Use a framework package instead (e.g. {{suggestions}}).'
901
+ }
902
+ },
903
+ create(context) {
904
+ return {
905
+ ImportDeclaration(node) {
906
+ let packageName = node.source.value;
907
+ if (typeof packageName == "string" && packageName in rendererToFrameworks) {
908
+ let suggestions = rendererToFrameworks[packageName];
909
+ context.report({
910
+ node,
911
+ messageId: "noRendererPackages",
912
+ data: {
913
+ rendererPackage: packageName,
914
+ suggestions: suggestions.join(", ")
915
+ }
916
+ });
917
+ }
918
+ }
919
+ };
920
+ }
921
+ });
922
+
923
+ // src/rules/no-stories-of.ts
924
+ var no_stories_of_default = createStorybookRule({
925
+ name: "no-stories-of",
926
+ defaultOptions: [],
927
+ meta: {
928
+ type: "problem",
929
+ severity: "error",
930
+ docs: {
931
+ description: "storiesOf is deprecated and should not be used",
932
+ categories: ["csf-strict" /* CSF_STRICT */]
933
+ },
934
+ messages: {
935
+ doNotUseStoriesOf: "storiesOf is deprecated and should not be used"
936
+ },
937
+ schema: []
938
+ },
939
+ create(context) {
940
+ return {
941
+ ImportSpecifier(node) {
942
+ "name" in node.imported && node.imported.name === "storiesOf" && context.report({
943
+ node,
944
+ messageId: "doNotUseStoriesOf"
945
+ });
946
+ }
947
+ };
948
+ }
949
+ });
950
+
951
+ // src/rules/no-title-property-in-meta.ts
952
+ var no_title_property_in_meta_default = createStorybookRule({
953
+ name: "no-title-property-in-meta",
954
+ defaultOptions: [],
955
+ meta: {
956
+ type: "problem",
957
+ fixable: "code",
958
+ hasSuggestions: !0,
959
+ severity: "error",
960
+ docs: {
961
+ description: "Do not define a title in meta",
962
+ categories: ["csf-strict" /* CSF_STRICT */]
963
+ },
964
+ messages: {
965
+ removeTitleInMeta: "Remove title property from meta",
966
+ noTitleInMeta: "CSF3 does not need a title in meta"
967
+ },
968
+ schema: []
969
+ },
970
+ create: function(context) {
971
+ return {
972
+ ExportDefaultDeclaration: function(node) {
973
+ let meta = getMetaObjectExpression(node, context);
974
+ if (!meta)
975
+ return null;
976
+ let titleNode = meta.properties.find(
977
+ (prop) => !isSpreadElement(prop) && "name" in prop.key && prop.key?.name === "title"
978
+ );
979
+ titleNode && context.report({
980
+ node: titleNode,
981
+ messageId: "noTitleInMeta",
982
+ suggest: [
983
+ {
984
+ messageId: "removeTitleInMeta",
985
+ fix(fixer) {
986
+ let hasComma = context.sourceCode.text.slice(
987
+ titleNode.range[0],
988
+ titleNode.range[1] + 1
989
+ ).slice(-1) === ",", propertyRange = [
990
+ titleNode.range[0],
991
+ hasComma ? titleNode.range[1] + 1 : titleNode.range[1]
992
+ ];
993
+ return fixer.removeRange(propertyRange);
994
+ }
995
+ }
996
+ ]
997
+ });
998
+ }
999
+ };
1000
+ }
1001
+ });
1002
+
1003
+ // src/rules/no-uninstalled-addons.ts
1004
+ var import_ts_dedent = __toESM(require_dist(), 1);
1005
+ import { readFileSync } from "fs";
1006
+ import { relative, resolve, sep } from "path";
1007
+ var no_uninstalled_addons_default = createStorybookRule({
1008
+ name: "no-uninstalled-addons",
1009
+ defaultOptions: [
1010
+ {
1011
+ packageJsonLocation: "",
1012
+ ignore: []
1013
+ }
1014
+ ],
1015
+ meta: {
1016
+ type: "problem",
1017
+ severity: "error",
1018
+ docs: {
1019
+ description: "This rule identifies storybook addons that are invalid because they are either not installed or contain a typo in their name.",
1020
+ categories: ["recommended" /* RECOMMENDED */]
1021
+ },
1022
+ messages: {
1023
+ addonIsNotInstalled: "The {{ addonName }} is not installed in {{packageJsonPath}}. Did you forget to install it or is your package.json in a different location?"
1024
+ },
1025
+ schema: [
1026
+ {
1027
+ type: "object",
1028
+ properties: {
1029
+ packageJsonLocation: {
1030
+ type: "string"
1031
+ },
1032
+ ignore: {
1033
+ type: "array",
1034
+ items: {
1035
+ type: "string"
1036
+ }
1037
+ }
1038
+ }
1039
+ }
1040
+ ]
1041
+ },
1042
+ create(context) {
1043
+ let { packageJsonLocation, ignore } = context.options.reduce(
1044
+ (acc, val) => ({
1045
+ packageJsonLocation: val.packageJsonLocation || acc.packageJsonLocation,
1046
+ ignore: val.ignore || acc.ignore
1047
+ }),
1048
+ { packageJsonLocation: "", ignore: [] }
1049
+ );
1050
+ function excludeNullable(item) {
1051
+ return !!item;
1052
+ }
1053
+ let mergeDepsWithDevDeps = (packageJson) => {
1054
+ let deps = Object.keys(packageJson.dependencies || {}), devDeps = Object.keys(packageJson.devDependencies || {});
1055
+ return [...deps, ...devDeps];
1056
+ }, isAddonInstalled = (addon, installedAddons) => {
1057
+ let addonName = addon.replace(/\.[mc]?js$/, "").replace(/\/register$/, "").replace(/\/preset$/, "");
1058
+ return installedAddons.includes(addonName);
1059
+ }, filterLocalAddons = (addon) => !((addonName) => addonName.startsWith(".") || addonName.startsWith("/") || // for local Windows files e.g. (C: F: D:)
1060
+ /\w:.*/.test(addonName) || addonName.startsWith("\\"))(addon), areThereAddonsNotInstalled = (addons, installedSbAddons) => {
1061
+ let result = addons.filter(filterLocalAddons).filter((addon) => !isAddonInstalled(addon, installedSbAddons) && !ignore.includes(addon)).map((addon) => ({ name: addon }));
1062
+ return result.length ? result : !1;
1063
+ }, getPackageJson = (path2) => {
1064
+ let packageJson = {
1065
+ devDependencies: {},
1066
+ dependencies: {}
1067
+ };
1068
+ try {
1069
+ let file = readFileSync(path2, "utf8"), parsedFile = JSON.parse(file);
1070
+ packageJson.dependencies = parsedFile.dependencies || {}, packageJson.devDependencies = parsedFile.devDependencies || {};
1071
+ } catch {
1072
+ throw new Error(
1073
+ import_ts_dedent.dedent`The provided path in your eslintrc.json - ${path2} 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.
1074
+
1075
+ Read more at: https://github.com/storybookjs/storybook/blob/next/code/lib/eslint-plugin/docs/rules/no-uninstalled-addons.md
1076
+ `
1077
+ );
1078
+ }
1079
+ return packageJson;
1080
+ }, extractAllAddonsFromTheStorybookConfig = (addonsExpression) => {
1081
+ if (addonsExpression?.elements) {
1082
+ let nodesWithAddons = addonsExpression.elements.map((elem) => isLiteral(elem) ? { value: elem.value, node: elem } : void 0).filter(excludeNullable), listOfAddonsInString = nodesWithAddons.map((elem) => elem.value), nodesWithAddonsInObj = addonsExpression.elements.map((elem) => isObjectExpression(elem) ? elem : { properties: [] }).map((elem) => {
1083
+ let property = elem.properties.find(
1084
+ (prop) => isProperty(prop) && isIdentifier(prop.key) && prop.key.name === "name"
1085
+ );
1086
+ return isLiteral(property?.value) ? { value: property.value.value, node: property.value } : void 0;
1087
+ }).filter(excludeNullable), listOfAddonsInObj = nodesWithAddonsInObj.map((elem) => elem.value), listOfAddons = [...listOfAddonsInString, ...listOfAddonsInObj], listOfAddonElements = [...nodesWithAddons, ...nodesWithAddonsInObj];
1088
+ return { listOfAddons, listOfAddonElements };
1089
+ }
1090
+ return { listOfAddons: [], listOfAddonElements: [] };
1091
+ };
1092
+ function reportUninstalledAddons(addonsProp) {
1093
+ let packageJsonPath = resolve(packageJsonLocation || "./package.json"), packageJsonObject;
1094
+ try {
1095
+ packageJsonObject = getPackageJson(packageJsonPath);
1096
+ } catch (e) {
1097
+ throw new Error(e);
1098
+ }
1099
+ let depsAndDevDeps = mergeDepsWithDevDeps(packageJsonObject), { listOfAddons, listOfAddonElements } = extractAllAddonsFromTheStorybookConfig(addonsProp), result = areThereAddonsNotInstalled(listOfAddons, depsAndDevDeps);
1100
+ if (result) {
1101
+ let elemsWithErrors = listOfAddonElements.filter(
1102
+ (elem) => !!result.find((addon) => addon.name === elem.value)
1103
+ ), currentPackageJsonPath = `${process.cwd().split(sep).pop()}${sep}${relative(process.cwd(), packageJsonLocation)}`;
1104
+ elemsWithErrors.forEach((elem) => {
1105
+ context.report({
1106
+ node: elem.node,
1107
+ messageId: "addonIsNotInstalled",
1108
+ data: {
1109
+ addonName: elem.value,
1110
+ packageJsonPath: currentPackageJsonPath
1111
+ }
1112
+ });
1113
+ });
1114
+ }
1115
+ }
1116
+ function findAddonsPropAndReport(node) {
1117
+ let addonsProp = node.properties.find(
1118
+ (prop) => isProperty(prop) && isIdentifier(prop.key) && prop.key.name === "addons"
1119
+ );
1120
+ addonsProp?.value && isArrayExpression(addonsProp.value) && reportUninstalledAddons(addonsProp.value);
1121
+ }
1122
+ return {
1123
+ AssignmentExpression: function(node) {
1124
+ isObjectExpression(node.right) && findAddonsPropAndReport(node.right);
1125
+ },
1126
+ ExportDefaultDeclaration: function(node) {
1127
+ let meta = getMetaObjectExpression(node, context);
1128
+ if (!meta)
1129
+ return null;
1130
+ findAddonsPropAndReport(meta);
1131
+ },
1132
+ ExportNamedDeclaration: function(node) {
1133
+ let addonsProp = isVariableDeclaration(node.declaration) && node.declaration.declarations.find(
1134
+ (decl) => isVariableDeclarator(decl) && isIdentifier(decl.id) && decl.id.name === "addons"
1135
+ );
1136
+ addonsProp && isArrayExpression(addonsProp.init) && reportUninstalledAddons(addonsProp.init);
1137
+ }
1138
+ };
1139
+ }
1140
+ });
1141
+
1142
+ // src/rules/prefer-pascal-case.ts
1143
+ import { isExportStory as isExportStory2 } from "storybook/internal/csf";
1144
+ import { ASTUtils as ASTUtils4 } from "@typescript-eslint/utils";
1145
+ var prefer_pascal_case_default = createStorybookRule({
1146
+ name: "prefer-pascal-case",
1147
+ defaultOptions: [],
1148
+ meta: {
1149
+ type: "suggestion",
1150
+ fixable: "code",
1151
+ hasSuggestions: !0,
1152
+ severity: "warn",
1153
+ docs: {
1154
+ description: "Stories should use PascalCase",
1155
+ categories: ["recommended" /* RECOMMENDED */]
1156
+ },
1157
+ messages: {
1158
+ convertToPascalCase: "Use pascal case",
1159
+ usePascalCase: "The story should use PascalCase notation: {{name}}"
1160
+ },
1161
+ schema: []
1162
+ },
1163
+ create(context) {
1164
+ let isPascalCase = (str) => /^[A-Z]+([a-z0-9]?)+/.test(str), toPascalCase = (str) => str.replace(new RegExp(/[-_]+/, "g"), " ").replace(new RegExp(/[^\w\s]/, "g"), "").replace(
1165
+ new RegExp(/\s+(.)(\w+)/, "g"),
1166
+ (_, $2, $3) => `${$2.toUpperCase() + $3.toLowerCase()}`
1167
+ ).replace(new RegExp(/\s/, "g"), "").replace(new RegExp(/\w/), (s) => s.toUpperCase()), getModuleScope = () => {
1168
+ let { sourceCode } = context;
1169
+ return sourceCode.getScope ? sourceCode.scopeManager?.scopes?.find(
1170
+ (scope) => scope.type === "module"
1171
+ ) : context.getScope().childScopes.find((scope) => scope.type === "module");
1172
+ }, checkAndReportError = (id, nonStoryExportsConfig2 = {}) => {
1173
+ let { name } = id;
1174
+ if (!isExportStory2(name, nonStoryExportsConfig2) || name === "__namedExportsOrder")
1175
+ return null;
1176
+ !name.startsWith("_") && !isPascalCase(name) && context.report({
1177
+ node: id,
1178
+ messageId: "usePascalCase",
1179
+ data: {
1180
+ name
1181
+ },
1182
+ suggest: [
1183
+ {
1184
+ messageId: "convertToPascalCase",
1185
+ *fix(fixer) {
1186
+ let suffix = context.sourceCode.text.slice(id.range[0], id.range[1]).substring(name.length), pascal = toPascalCase(name);
1187
+ yield fixer.replaceTextRange(id.range, pascal + suffix);
1188
+ let scope = getModuleScope();
1189
+ if (scope) {
1190
+ let variable = ASTUtils4.findVariable(scope, name), referenceCount = variable?.references?.length || 0;
1191
+ for (let i = 0; i < referenceCount; i++) {
1192
+ let ref = variable?.references[i];
1193
+ ref && !ref.init && (yield fixer.replaceTextRange(ref.identifier.range, pascal));
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ ]
1199
+ });
1200
+ }, meta, nonStoryExportsConfig, namedExports = [], hasStoriesOfImport = !1;
1201
+ return {
1202
+ ImportSpecifier(node) {
1203
+ "name" in node.imported && node.imported.name === "storiesOf" && (hasStoriesOfImport = !0);
1204
+ },
1205
+ ExportDefaultDeclaration: function(node) {
1206
+ if (meta = getMetaObjectExpression(node, context), meta)
1207
+ try {
1208
+ nonStoryExportsConfig = {
1209
+ excludeStories: getDescriptor(meta, "excludeStories"),
1210
+ includeStories: getDescriptor(meta, "includeStories")
1211
+ };
1212
+ } catch {
1213
+ }
1214
+ },
1215
+ ExportNamedDeclaration: function(node) {
1216
+ if (!node.declaration)
1217
+ return;
1218
+ let decl = node.declaration;
1219
+ if (isVariableDeclaration(decl)) {
1220
+ let declaration = decl.declarations[0];
1221
+ if (declaration == null)
1222
+ return;
1223
+ let { id } = declaration;
1224
+ isIdentifier(id) && namedExports.push(id);
1225
+ }
1226
+ },
1227
+ "Program:exit": function() {
1228
+ namedExports.length && !hasStoriesOfImport && namedExports.forEach((n) => checkAndReportError(n, nonStoryExportsConfig));
1229
+ }
1230
+ };
1231
+ }
1232
+ });
1233
+
1234
+ // src/rules/story-exports.ts
1235
+ var story_exports_default = createStorybookRule({
1236
+ name: "story-exports",
1237
+ defaultOptions: [],
1238
+ meta: {
1239
+ type: "problem",
1240
+ severity: "error",
1241
+ docs: {
1242
+ description: "A story file must contain at least one story export",
1243
+ categories: ["recommended" /* RECOMMENDED */, "csf" /* CSF */]
1244
+ },
1245
+ messages: {
1246
+ shouldHaveStoryExport: "The file should have at least one story export",
1247
+ shouldHaveStoryExportWithFilters: "The file should have at least one story export. Make sure the includeStories/excludeStories you defined are correct, otherwise Storybook will not use any stories for this file.",
1248
+ addStoryExport: "Add a story export"
1249
+ },
1250
+ fixable: void 0,
1251
+ // change to 'code' once we have autofixes
1252
+ schema: []
1253
+ },
1254
+ create(context) {
1255
+ let hasStoriesOfImport = !1, nonStoryExportsConfig = {}, meta, namedExports = [];
1256
+ return {
1257
+ ImportSpecifier(node) {
1258
+ "name" in node.imported && node.imported.name === "storiesOf" && (hasStoriesOfImport = !0);
1259
+ },
1260
+ ExportDefaultDeclaration: function(node) {
1261
+ if (meta = getMetaObjectExpression(node, context), meta)
1262
+ try {
1263
+ nonStoryExportsConfig = {
1264
+ excludeStories: getDescriptor(meta, "excludeStories"),
1265
+ includeStories: getDescriptor(meta, "includeStories")
1266
+ };
1267
+ } catch {
1268
+ }
1269
+ },
1270
+ ExportNamedDeclaration: function(node) {
1271
+ namedExports.push(...getAllNamedExports(node));
1272
+ },
1273
+ "Program:exit": function(program) {
1274
+ if (hasStoriesOfImport || !meta || namedExports.filter(
1275
+ (exp) => isValidStoryExport(exp, nonStoryExportsConfig)
1276
+ ).length)
1277
+ return;
1278
+ let node = program.body.find((n) => !isImportDeclaration(n)) || program.body[0] || program, hasFilter = nonStoryExportsConfig.includeStories || nonStoryExportsConfig.excludeStories, report = {
1279
+ node,
1280
+ messageId: hasFilter ? "shouldHaveStoryExportWithFilters" : "shouldHaveStoryExport"
1281
+ // fix,
1282
+ };
1283
+ context.report(report);
1284
+ }
1285
+ };
1286
+ }
1287
+ });
1288
+
1289
+ // src/rules/use-storybook-expect.ts
1290
+ var use_storybook_expect_default = createStorybookRule({
1291
+ name: "use-storybook-expect",
1292
+ defaultOptions: [],
1293
+ meta: {
1294
+ type: "suggestion",
1295
+ fixable: "code",
1296
+ schema: [],
1297
+ severity: "error",
1298
+ docs: {
1299
+ description: "Use expect from `@storybook/test`, `storybook/test` or `@storybook/jest`",
1300
+ categories: ["addon-interactions" /* ADDON_INTERACTIONS */, "recommended" /* RECOMMENDED */]
1301
+ },
1302
+ messages: {
1303
+ useExpectFromStorybook: "Do not use global expect directly in the story. You should import it from `@storybook/test` (preferrably) or `@storybook/jest` instead."
1304
+ }
1305
+ },
1306
+ create(context) {
1307
+ let isExpectFromStorybookImported = (node) => {
1308
+ let { value: packageName } = node.source;
1309
+ return (packageName === "@storybook/jest" || packageName === "@storybook/test" || packageName === "storybook/test") && node.specifiers.find(
1310
+ (spec) => isImportSpecifier(spec) && "name" in spec.imported && spec.imported.name === "expect"
1311
+ );
1312
+ }, isImportingFromStorybookExpect = !1, expectInvocations = [];
1313
+ return {
1314
+ ImportDeclaration(node) {
1315
+ isExpectFromStorybookImported(node) && (isImportingFromStorybookExpect = !0);
1316
+ },
1317
+ CallExpression(node) {
1318
+ if (!isIdentifier(node.callee))
1319
+ return null;
1320
+ node.callee.name === "expect" && expectInvocations.push(node.callee);
1321
+ },
1322
+ "Program:exit": function() {
1323
+ !isImportingFromStorybookExpect && expectInvocations.length && expectInvocations.forEach((node) => {
1324
+ context.report({
1325
+ node,
1326
+ messageId: "useExpectFromStorybook"
1327
+ });
1328
+ });
1329
+ }
1330
+ };
1331
+ }
1332
+ });
1333
+
1334
+ // src/rules/use-storybook-testing-library.ts
1335
+ var use_storybook_testing_library_default = createStorybookRule({
1336
+ name: "use-storybook-testing-library",
1337
+ defaultOptions: [],
1338
+ meta: {
1339
+ type: "suggestion",
1340
+ fixable: "code",
1341
+ hasSuggestions: !0,
1342
+ severity: "error",
1343
+ docs: {
1344
+ description: "Do not use testing-library directly on stories",
1345
+ categories: ["addon-interactions" /* ADDON_INTERACTIONS */, "recommended" /* RECOMMENDED */]
1346
+ },
1347
+ schema: [],
1348
+ messages: {
1349
+ updateImports: "Update imports",
1350
+ dontUseTestingLibraryDirectly: "Do not use `{{library}}` directly in the story. You should import the functions from `@storybook/test` (preferrably) or `@storybook/testing-library` instead."
1351
+ }
1352
+ },
1353
+ create(context) {
1354
+ let getRangeWithoutQuotes = (source) => [
1355
+ // Not sure how to improve this. If I use node.source.range
1356
+ // it will eat the quotes and we do not want to specify whether the quotes are single or double
1357
+ source.range[0] + 1,
1358
+ source.range[1] - 1
1359
+ ], hasDefaultImport = (specifiers) => specifiers.find((s) => isImportDefaultSpecifier(s)), getSpecifiers = (node) => {
1360
+ let { specifiers } = node;
1361
+ if (!specifiers[0])
1362
+ return null;
1363
+ let start = specifiers[0].range[0], previousSpecifier = specifiers[specifiers.length - 1];
1364
+ if (!previousSpecifier)
1365
+ return null;
1366
+ let end = previousSpecifier.range[1], fullText = context.sourceCode.text, importEnd = node.range[1], closingBrace = fullText.indexOf("}", end - 1);
1367
+ closingBrace > -1 && closingBrace <= importEnd && (end = closingBrace + 1);
1368
+ let text = fullText.substring(start, end);
1369
+ return { range: [start, end], text };
1370
+ }, fixSpecifiers = (specifiersText) => `{ ${specifiersText.replace("{", "").replace("}", "").replace(/\s\s+/g, " ").trim()} }`;
1371
+ return {
1372
+ ImportDeclaration(node) {
1373
+ node.source.value.includes("@testing-library") && context.report({
1374
+ node,
1375
+ messageId: "dontUseTestingLibraryDirectly",
1376
+ data: {
1377
+ library: node.source.value
1378
+ },
1379
+ *fix(fixer) {
1380
+ if (yield fixer.replaceTextRange(
1381
+ getRangeWithoutQuotes(node.source),
1382
+ "@storybook/testing-library"
1383
+ ), hasDefaultImport(node.specifiers)) {
1384
+ let specifiers = getSpecifiers(node);
1385
+ if (specifiers) {
1386
+ let { range, text } = specifiers;
1387
+ yield fixer.replaceTextRange(range, fixSpecifiers(text));
1388
+ }
1389
+ }
1390
+ },
1391
+ suggest: [
1392
+ {
1393
+ messageId: "updateImports",
1394
+ *fix(fixer) {
1395
+ if (yield fixer.replaceTextRange(
1396
+ getRangeWithoutQuotes(node.source),
1397
+ "@storybook/testing-library"
1398
+ ), hasDefaultImport(node.specifiers)) {
1399
+ let specifiers = getSpecifiers(node);
1400
+ if (specifiers) {
1401
+ let { range, text } = specifiers;
1402
+ yield fixer.replaceTextRange(range, fixSpecifiers(text));
1403
+ }
1404
+ }
1405
+ }
1406
+ }
1407
+ ]
1408
+ });
1409
+ }
1410
+ };
1411
+ }
1412
+ });
1413
+
1414
+ // src/index.ts
1415
+ var configs = {
1416
+ // eslintrc configs
1417
+ csf: csf_default,
1418
+ "csf-strict": csf_strict_default,
1419
+ "addon-interactions": addon_interactions_default,
1420
+ recommended: recommended_default2,
1421
+ // flat configs
1422
+ "flat/csf": csf_default2,
1423
+ "flat/csf-strict": csf_strict_default2,
1424
+ "flat/addon-interactions": addon_interactions_default2,
1425
+ "flat/recommended": recommended_default
1426
+ }, rules = {
1427
+ "await-interactions": await_interactions_default,
1428
+ "context-in-play-function": context_in_play_function_default,
1429
+ "csf-component": csf_component_default,
1430
+ "default-exports": default_exports_default,
1431
+ "hierarchy-separator": hierarchy_separator_default,
1432
+ "meta-inline-properties": meta_inline_properties_default,
1433
+ "meta-satisfies-type": meta_satisfies_type_default,
1434
+ "no-redundant-story-name": no_redundant_story_name_default,
1435
+ "no-renderer-packages": no_renderer_packages_default,
1436
+ "no-stories-of": no_stories_of_default,
1437
+ "no-title-property-in-meta": no_title_property_in_meta_default,
1438
+ "no-uninstalled-addons": no_uninstalled_addons_default,
1439
+ "prefer-pascal-case": prefer_pascal_case_default,
1440
+ "story-exports": story_exports_default,
1441
+ "use-storybook-expect": use_storybook_expect_default,
1442
+ "use-storybook-testing-library": use_storybook_testing_library_default
1443
+ }, index_default = {
1444
+ configs,
1445
+ rules
1446
+ };
1447
+ export {
1448
+ configs,
1449
+ index_default as default,
1450
+ rules
1451
+ };