@suss/contract-storybook 0.33.0 → 0.34.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/README.md +16 -16
- package/dist/index.cjs +2 -5
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +2 -5
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
# @suss/contract-storybook
|
|
2
2
|
|
|
3
|
-
Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and
|
|
3
|
+
Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and reports where the two disagree.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package builds suss `BehavioralSummary[]` from [Storybook](https://storybook.js.org/) CSF3 story files. A story is a specification somebody wrote by hand: this component supports this set of props. Reading stories as contracts lets you check whether a component accepts the args every story passes, and whether every branch inferred from the component has a story that reaches it.
|
|
6
6
|
|
|
7
7
|
## What this package reads
|
|
8
8
|
|
|
9
|
-
`@suss/contract-storybook` parses `.stories.ts` and `.stories.tsx` files with ts-morph and
|
|
9
|
+
`@suss/contract-storybook` parses `.stories.ts` and `.stories.tsx` files with ts-morph, and reads two things in each one.
|
|
10
10
|
|
|
11
|
-
The default export gives the component under test. The reader walks the default
|
|
11
|
+
The default export gives the component under test. The reader walks the declarations of the default export's symbol, looking for an object literal with a `component` property. That covers `export default { component: Button }`, `const meta = { component: Button }; export default meta;` and `export default { ... } satisfies Meta<typeof Button>`. Parentheses, `as` expressions, and an identifier that points at a local variable are followed through to the literal. A file whose default export never resolves to such an object is skipped, and the rest of the run continues.
|
|
12
12
|
|
|
13
|
-
Every other named export whose initializer resolves to an object literal is a story. Its `args` object literal becomes the story's arguments,
|
|
13
|
+
Every other named export whose initializer resolves to an object literal is a story. Its `args` object literal becomes the story's arguments, and each property's value is recorded as the source text you wrote. A shorthand property (`{ disabled }`) records its own name.
|
|
14
14
|
|
|
15
15
|
## What it produces
|
|
16
16
|
|
|
17
17
|
One `component`-kind summary per named story export:
|
|
18
18
|
|
|
19
|
-
- `identity.name` is `Component.Story`, with `exportPath` set to the story's export name and a function-call boundary binding
|
|
20
|
-
- One input per arg. The input's `role` is the arg name, and its shape is a `ref` whose name is the arg's source text, so a reader can see the
|
|
19
|
+
- `identity.name` is `Component.Story`, with `exportPath` set to the story's export name, and a function-call boundary binding with `transport: "in-process"`, `recognition: "react"`, and the component identifier as the export name.
|
|
20
|
+
- One input per arg. The input's `role` is the arg name, and its shape is a `ref` whose name is the arg's source text, so a reader can see the value that was written.
|
|
21
21
|
- One default transition whose output is a render of the component. The reader does not evaluate the render, so the rendered tree is left unset.
|
|
22
|
-
- `metadata.component.storybook` with the story name, the component name, the args map, and `provenance: "independent"
|
|
23
|
-
- Confidence is `derived` at `medium`.
|
|
22
|
+
- `metadata.component.storybook` with the story name, the component name, the args map, and `provenance: "independent"`. The provenance is what lets a story act as a check on an inferred component summary, since it was written separately from the component.
|
|
23
|
+
- Confidence is `derived` at `medium`. People write stories, and a story is authoritative about what it covers, but stories do not list everything a component does.
|
|
24
24
|
|
|
25
25
|
## What it does not read
|
|
26
26
|
|
|
27
|
-
- **`play` functions.** The
|
|
28
|
-
- **`argTypes`.**
|
|
29
|
-
- **`decorators` and `parameters`.** Storybook runtime
|
|
27
|
+
- **`play` functions.** The sequence of events that drives an interactive story is not recorded.
|
|
28
|
+
- **`argTypes`.** Control types and option lists per arg could support stricter type checking, but they are not read today.
|
|
29
|
+
- **`decorators` and `parameters`.** These are Storybook runtime setup and do not describe the component's behavior.
|
|
30
30
|
- **CSF1 and MDX stories.** CSF3 is the supported format.
|
|
31
31
|
- **The component's own module.** The reader keeps the `component` identifier as written and does not follow the import to where the component is defined.
|
|
32
|
-
- **Arg values as structured shapes.** An arg's value stays source text inside a `ref` shape
|
|
32
|
+
- **Arg values as structured shapes.** An arg's value stays as source text inside a `ref` shape, and is not parsed into a `TypeShape`.
|
|
33
33
|
|
|
34
34
|
## Worked example
|
|
35
35
|
|
|
@@ -53,9 +53,9 @@ suss contract --from storybook src/components -o summaries/stories.json
|
|
|
53
53
|
suss check summaries/app.json summaries/stories.json
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
The path
|
|
56
|
+
The path can be one `.stories.ts[x]` file or a directory, which the CLI searches recursively for story files. The file above produces two summaries, `Button.Primary` and `Button.Disabled`, each with `variant` and `disabled` inputs.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
To control the set of files, or the root that relative paths are computed from, call it from code:
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
61
|
import { generateSummariesFromStories } from "@suss/contract-storybook";
|
|
@@ -68,7 +68,7 @@ const summaries = generateSummariesFromStories(
|
|
|
68
68
|
|
|
69
69
|
## Where it fits in suss
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
The package depends on `@suss/behavioral-ir` for the IR types it produces and on `@suss/adapter-typescript` for export resolution, with `ts-morph` as a peer dependency. It is the only contract reader that parses TypeScript, because CSF is TypeScript. It still reads a declared artifact, and does not infer behavior from a component's implementation.
|
|
72
72
|
|
|
73
73
|
## More
|
|
74
74
|
|
package/dist/index.cjs
CHANGED
|
@@ -129,11 +129,8 @@ function buildSummary(story, meta, filePath) {
|
|
|
129
129
|
name,
|
|
130
130
|
position: 0,
|
|
131
131
|
role: name,
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
// this to a structured literal shape is a follow-up (would need to
|
|
135
|
-
// parse each arg's source into a TypeShape; for v0 we surface the
|
|
136
|
-
// text).
|
|
132
|
+
// The arg's value goes in `ref.name` so a reader can see what the
|
|
133
|
+
// story passes. It is never parsed into a structured TypeShape.
|
|
137
134
|
shape: { type: "ref", name: value }
|
|
138
135
|
}));
|
|
139
136
|
const transition = {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Reads Storybook CSF3 story files into one component summary per story.
|
|
5
|
+
* A story is a hand-written claim that its component supports one set of
|
|
6
|
+
* props. Comparing stories with the component's inferred summary shows
|
|
7
|
+
* whether the component accepts every story's args, and whether each
|
|
8
|
+
* inferred branch has a story that reaches it.
|
|
9
|
+
*
|
|
10
|
+
* Confidence is `medium`, because a story is accurate about what it covers
|
|
11
|
+
* but stories never list everything a component does. The README lists
|
|
12
|
+
* what the reader leaves out.
|
|
13
|
+
*/
|
|
14
|
+
|
|
3
15
|
interface StorybookStubOptions {
|
|
4
16
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
17
|
+
* Each summary's `location.file` is relative to this, so it is the same
|
|
18
|
+
* on every machine. Defaults to the working directory.
|
|
7
19
|
*/
|
|
8
20
|
projectRoot?: string;
|
|
9
21
|
}
|
|
10
22
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
23
|
+
* Reads `.stories.ts` and `.stories.tsx` files and returns one summary per
|
|
24
|
+
* named story export. A file whose default export has no `component` is
|
|
25
|
+
* skipped.
|
|
13
26
|
*/
|
|
14
27
|
declare function generateSummariesFromStories(filePaths: string[], options?: StorybookStubOptions): BehavioralSummary[];
|
|
15
28
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Reads Storybook CSF3 story files into one component summary per story.
|
|
5
|
+
* A story is a hand-written claim that its component supports one set of
|
|
6
|
+
* props. Comparing stories with the component's inferred summary shows
|
|
7
|
+
* whether the component accepts every story's args, and whether each
|
|
8
|
+
* inferred branch has a story that reaches it.
|
|
9
|
+
*
|
|
10
|
+
* Confidence is `medium`, because a story is accurate about what it covers
|
|
11
|
+
* but stories never list everything a component does. The README lists
|
|
12
|
+
* what the reader leaves out.
|
|
13
|
+
*/
|
|
14
|
+
|
|
3
15
|
interface StorybookStubOptions {
|
|
4
16
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
17
|
+
* Each summary's `location.file` is relative to this, so it is the same
|
|
18
|
+
* on every machine. Defaults to the working directory.
|
|
7
19
|
*/
|
|
8
20
|
projectRoot?: string;
|
|
9
21
|
}
|
|
10
22
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
23
|
+
* Reads `.stories.ts` and `.stories.tsx` files and returns one summary per
|
|
24
|
+
* named story export. A file whose default export has no `component` is
|
|
25
|
+
* skipped.
|
|
13
26
|
*/
|
|
14
27
|
declare function generateSummariesFromStories(filePaths: string[], options?: StorybookStubOptions): BehavioralSummary[];
|
|
15
28
|
|
package/dist/index.js
CHANGED
|
@@ -107,11 +107,8 @@ function buildSummary(story, meta, filePath) {
|
|
|
107
107
|
name,
|
|
108
108
|
position: 0,
|
|
109
109
|
role: name,
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// this to a structured literal shape is a follow-up (would need to
|
|
113
|
-
// parse each arg's source into a TypeShape; for v0 we surface the
|
|
114
|
-
// text).
|
|
110
|
+
// The arg's value goes in `ref.name` so a reader can see what the
|
|
111
|
+
// story passes. It is never parsed into a structured TypeShape.
|
|
115
112
|
shape: { type: "ref", name: value }
|
|
116
113
|
}));
|
|
117
114
|
const transition = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/contract-storybook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Turn Storybook CSF stories into a suss contract and check the props they pass against the React components that receive them.",
|
|
6
6
|
"keywords": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"license": "Apache-2.0",
|
|
16
16
|
"author": "Nimbus Cloud AI LLC",
|
|
17
|
-
"homepage": "https://
|
|
17
|
+
"homepage": "https://suss.sh",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "git+https://github.com/nimbuscloud-ai/suss.git",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"typecheck": "tsc --noEmit"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@suss/adapter-typescript": "0.
|
|
51
|
-
"@suss/behavioral-ir": "0.
|
|
50
|
+
"@suss/adapter-typescript": "0.34.0",
|
|
51
|
+
"@suss/behavioral-ir": "0.34.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"tsup": "*",
|