@trackunit/eslint-plugin-trackunit 0.6.165 → 0.6.167
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 0.6.167 (2026-10-07)
|
|
2
|
+
|
|
3
|
+
### 🧱 Updated Dependencies
|
|
4
|
+
|
|
5
|
+
- Updated css-classname-utils to 0.0.68
|
|
6
|
+
- Updated shared-utils to 1.19.2
|
|
7
|
+
|
|
8
|
+
## 0.6.166 (2026-10-07)
|
|
9
|
+
|
|
10
|
+
### 🧱 Updated Dependencies
|
|
11
|
+
|
|
12
|
+
- Updated css-classname-utils to 0.0.67
|
|
13
|
+
- Updated shared-utils to 1.19.1
|
|
14
|
+
|
|
1
15
|
## 0.6.165 (2026-10-06)
|
|
2
16
|
|
|
3
17
|
### 🧱 Updated Dependencies
|
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ project.
|
|
|
97
97
|
| ----------------------------------------------------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
98
98
|
| `@trackunit/storybook/require-folder-landing-page` | error | no | Requires a `*.docs.mdx` landing page (`<Meta title="...">`) for every ancestor folder implied by a story's `title` — including titles read from CSF factory `<binding>.meta({...})` calls. Without a landing page, Storybook renders an empty grouping node in the sidebar. |
|
|
99
99
|
| `@trackunit/require-storybook-folder-landing-page` | not enabled by default | no | Deprecated alias of `@trackunit/storybook/require-folder-landing-page` — same rule implementation, kept only so configs still referencing the pre-namespace id keep working. Neither the published `react` preset nor the internal `storybook` preset enables it; a consumer must re-enable it explicitly. |
|
|
100
|
-
| `@trackunit/storybook/require-csf-factory` |
|
|
100
|
+
| `@trackunit/storybook/require-csf-factory` | error | no | Detection-only: flags story files in the legacy `export default {...}` shape (`oldFormat`), files mixing that shape with a factory `.meta({...})` call (`mixedFormat`), and files with neither signal (`missingMeta`). Stays silent on files already in the CSF **factory** format (`<binding>.meta({...})` + `<meta>.story({...})`). |
|
|
101
101
|
| `@trackunit/storybook/no-local-meta-component` | error | no | Detection-only: flags a meta `component` (in `export default {...}`, `const meta = {...}; export default meta`, or any `<binding>.meta({...})`) that resolves to a function/class declared in the story file, an inline component, a wrapper call around a local (`memo(Local)`), or an import from another `*.stories` module. Docgen skips `*.stories.*`, so Controls loses JSDoc prop descriptions and "Show code" names a story-only identifier. Fix by pointing at the real component (`component: MultiSelectField<Option>` for generics) or moving the demo into a sibling `*.demo.tsx`. |
|
|
102
102
|
| `storybook/*` (upstream `eslint-plugin-storybook@^10.6.0` `flat/recommended`) | per-rule (upstream defaults), with `storybook/default-exports` forced `off` | per-rule | Upstream Storybook lint rules, spread only by the internal `storybook` preset — never by the published `react` preset. `storybook/default-exports` is turned back off immediately after the spread because it contradicts the CSF factory format's implicit default export via the `meta` object; `require-csf-factory` above already reports a story file with no meta/default export at all, so no detection guarantee is lost by disabling it. |
|
|
103
103
|
|
|
@@ -113,7 +113,7 @@ module.exports = [
|
|
|
113
113
|
{
|
|
114
114
|
rules: {
|
|
115
115
|
"@trackunit/storybook/require-folder-landing-page": "error",
|
|
116
|
-
"@trackunit/storybook/require-csf-factory": "
|
|
116
|
+
"@trackunit/storybook/require-csf-factory": "error",
|
|
117
117
|
"@trackunit/storybook/no-local-meta-component": "error",
|
|
118
118
|
},
|
|
119
119
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/eslint-plugin-trackunit",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.167",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"repository": "https://github.com/Trackunit/manager",
|
|
6
6
|
"engines": {
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@nx/eslint-plugin": "23.1.0",
|
|
11
|
-
"@trackunit/css-classname-utils": "0.0.
|
|
12
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
13
|
-
"@typescript-eslint/utils": "8.
|
|
11
|
+
"@trackunit/css-classname-utils": "0.0.68",
|
|
12
|
+
"@typescript-eslint/eslint-plugin": "8.70.1",
|
|
13
|
+
"@typescript-eslint/utils": "8.70.1",
|
|
14
14
|
"eslint-config-prettier": "^10.1.8",
|
|
15
15
|
"eslint-plugin-import": "2.32.0",
|
|
16
16
|
"eslint-plugin-jest": "^29.15.2",
|
|
@@ -5,7 +5,8 @@ exports.moduleBoundaryRules = {
|
|
|
5
5
|
"@nx/enforce-module-boundaries": [
|
|
6
6
|
"error",
|
|
7
7
|
{
|
|
8
|
-
|
|
8
|
+
// Ambient alias for the loading Storybook's preview (`typings/storybook-preview.d.ts`), not a project.
|
|
9
|
+
allow: ["@trackunit/storybook/preview"],
|
|
9
10
|
depConstraints: [
|
|
10
11
|
{
|
|
11
12
|
sourceTag: "type:app",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Report a `*.stories.*` file written in the legacy CSF (Component Story Format)
|
|
3
3
|
* default-export shape instead of Storybook's CSF **factory** format.
|
|
4
4
|
*
|
|
5
5
|
* Architecture: per-story-file rule, syntax-only (no TypeScript type information — see
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* meta;` variable), and no factory `.meta({...})` call anywhere in the file. Reported with
|
|
12
12
|
* `oldFormat`, pointing at the `export default` node.
|
|
13
13
|
* - **Factory format** (silent) — a call `<importedBinding>.meta({...})` (e.g.
|
|
14
|
-
* `const meta = preview.meta({ component, title });`)
|
|
14
|
+
* `const meta = preview.meta({ component, title });`) or
|
|
15
|
+
* `<importedBinding>.type<{ args }>().meta({...})`, where `<importedBinding>` resolves to
|
|
15
16
|
* an imported identifier, and NEITHER an `export default` NOR an old-style named
|
|
16
17
|
* object-literal story export (`export const Foo = { args: {...} };`, including a `satisfies` or
|
|
17
18
|
* `as` wrapper around that object) is present. Individual
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Report a `*.stories.*` file written in the legacy CSF (Component Story Format)
|
|
4
4
|
* default-export shape instead of Storybook's CSF **factory** format.
|
|
5
5
|
*
|
|
6
6
|
* Architecture: per-story-file rule, syntax-only (no TypeScript type information — see
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* meta;` variable), and no factory `.meta({...})` call anywhere in the file. Reported with
|
|
13
13
|
* `oldFormat`, pointing at the `export default` node.
|
|
14
14
|
* - **Factory format** (silent) — a call `<importedBinding>.meta({...})` (e.g.
|
|
15
|
-
* `const meta = preview.meta({ component, title });`)
|
|
15
|
+
* `const meta = preview.meta({ component, title });`) or
|
|
16
|
+
* `<importedBinding>.type<{ args }>().meta({...})`, where `<importedBinding>` resolves to
|
|
16
17
|
* an imported identifier, and NEITHER an `export default` NOR an old-style named
|
|
17
18
|
* object-literal story export (`export const Foo = { args: {...} };`, including a `satisfies` or
|
|
18
19
|
* `as` wrapper around that object) is present. Individual
|
|
@@ -75,28 +76,48 @@ const isAnalyzableStoryFile = (filename) => {
|
|
|
75
76
|
return STORY_FILE_REGEX.test(filename);
|
|
76
77
|
};
|
|
77
78
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
* The `<identifier>` a `.meta(...)` call is made on: `preview` in `preview.meta(...)` and in the
|
|
80
|
+
* typed escape hatch `preview.type<{ args }>().meta(...)`. Null for any other receiver.
|
|
81
|
+
*/
|
|
82
|
+
const getMetaReceiver = (receiver) => {
|
|
83
|
+
if (receiver.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
84
|
+
return receiver;
|
|
85
|
+
}
|
|
86
|
+
if (receiver.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
87
|
+
receiver.arguments.length === 0 &&
|
|
88
|
+
receiver.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
89
|
+
!receiver.callee.computed &&
|
|
90
|
+
receiver.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
91
|
+
receiver.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
92
|
+
receiver.callee.property.name === "type") {
|
|
93
|
+
return receiver.callee.object;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* True for a call `<identifier>.meta({...})` or `<identifier>.type<…>().meta({...})` where
|
|
99
|
+
* `<identifier>` resolves to an imported binding. Only the call *shape* is inspected — the object
|
|
100
|
+
* argument's properties (e.g. a generic/instantiation `component:` value) are never type-checked
|
|
101
|
+
* or destructured, so this rule stays syntax-only.
|
|
82
102
|
*/
|
|
83
103
|
const isFactoryMetaCall = (node, context) => {
|
|
84
104
|
const { callee } = node;
|
|
85
105
|
if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
86
106
|
return false;
|
|
87
107
|
}
|
|
88
|
-
if (callee.property.type !== utils_1.AST_NODE_TYPES.Identifier || callee.property.name !== "meta") {
|
|
108
|
+
if (callee.computed || callee.property.type !== utils_1.AST_NODE_TYPES.Identifier || callee.property.name !== "meta") {
|
|
89
109
|
return false;
|
|
90
110
|
}
|
|
91
|
-
|
|
111
|
+
const receiver = getMetaReceiver(callee.object);
|
|
112
|
+
if (receiver === null) {
|
|
92
113
|
return false;
|
|
93
114
|
}
|
|
94
115
|
const [firstArgument] = node.arguments;
|
|
95
116
|
if (firstArgument === undefined || firstArgument.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
96
117
|
return false;
|
|
97
118
|
}
|
|
98
|
-
const scope = context.sourceCode.getScope(
|
|
99
|
-
const variable = scope.references.find(ref => ref.identifier ===
|
|
119
|
+
const scope = context.sourceCode.getScope(receiver);
|
|
120
|
+
const variable = scope.references.find(ref => ref.identifier === receiver)?.resolved;
|
|
100
121
|
if (!variable) {
|
|
101
122
|
return false;
|
|
102
123
|
}
|
|
@@ -170,7 +191,7 @@ exports.requireCsfFactory = createRule({
|
|
|
170
191
|
meta: {
|
|
171
192
|
type: "problem",
|
|
172
193
|
docs: {
|
|
173
|
-
description: "
|
|
194
|
+
description: "Report Storybook story files that are not written in CSF factory format",
|
|
174
195
|
},
|
|
175
196
|
schema: [],
|
|
176
197
|
messages: {
|