@uniflowed/story 0.0.0-alpha.18 → 0.14.1

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.
Files changed (4) hide show
  1. package/collect.js +29 -21
  2. package/index.js +15 -22
  3. package/package.json +6 -6
  4. package/runner.js +8 -6
package/collect.js CHANGED
@@ -2,41 +2,39 @@
2
2
  //
3
3
  // `@uniflowed/story/collect`: which files are stories, and what is in them.
4
4
  //
5
- // A story file is `_uf.story.js`, beside the component it describes.
5
+ // A story file is `$story.js`, beside the component it describes.
6
6
  //
7
7
  // src/components/Button.js
8
- // src/components/_uf.story.js
8
+ // src/components/$story.js
9
9
  //
10
10
  // # The name is the repository's own grammar, not a second one
11
11
  //
12
- // uf already reserves `_uf.<role>[.<variant>].js` for the files the framework
12
+ // uf already reserves `$<role>[.<variant>].js` for the files the framework
13
13
  // gives meaning to, and `crates/uf_router/src/reserved.rs` is its single
14
14
  // source of truth: `uf create` generates those names, the router looks for
15
15
  // them, and `uf lint`'s `router/reserved-files` rejects the ones that do not
16
16
  // fit. A story file is exactly such a file — a name uf assigns meaning to, in
17
- // a project's own source tree — so it is spelled `_uf.story.js` and not
17
+ // a project's own source tree — so it is spelled `$story.js` and not
18
18
  // `*.stories.js`.
19
19
  //
20
20
  // The variants are the same vocabulary for the same reason, and the rule
21
21
  // about them is the router's too: only the default variant is the thing the
22
- // runner renders. `_uf.story.native.js` is a companion for a React Native
23
- // build, the way `_uf.page.native.js` is, and [`findStoryFiles`] leaves it
22
+ // runner renders. `$story.native.js` is a companion for a React Native
23
+ // build, the way `$page.native.js` is, and [`findStoryFiles`] leaves it
24
24
  // alone until there is a renderer that could mount it.
25
25
  //
26
- // **`story` is not yet one of the roles the Rust grammar defines.** Adding it
27
- // is one arm in `ReservedRole` in `crates/uf_router/src/reserved.rs`, and
28
- // this package cannot make that change from JavaScript. Until it lands,
29
- // `uf lint` reports `router/reserved-files` on every `_uf.story.js` — the
30
- // name is right and the linter has not been told. `index.js` lists it under
31
- // **Readiness** rather than leaving it to be discovered by whoever writes the
32
- // first story file.
26
+ // `story` is one of the roles `ReservedRole` in
27
+ // `crates/uf_router/src/reserved.rs` defines, so `uf lint`'s
28
+ // `router/reserved-files` accepts `$story.js` and its variants and still
29
+ // reports a misspelling such as `$stori.js`. The variant list below mirrors
30
+ // `ReservedVariant`, `client` included; a test pins it.
33
31
  //
34
32
  // # What makes a file a story file is the name, and what makes it valid is
35
33
  // the export
36
34
  //
37
35
  // Discovery is by name alone: a walk that had to read every file to find out
38
36
  // whether it declared stories would be a parse of the whole tree. Loading is
39
- // where a file is judged, and a `_uf.story.js` that exports no story set is
37
+ // where a file is judged, and a `$story.js` that exports no story set is
40
38
  // an error rather than an empty result — the name is a claim, and a file that
41
39
  // does not honour it is a mistake somebody made, not a fact about the
42
40
  // project.
@@ -72,17 +70,24 @@ import { isStorySet } from "./story.js";
72
70
  export const STORY_ROLE: "story" = "story";
73
71
 
74
72
  /** The name of a story file with no variant: the one the runner renders. */
75
- export const STORY_FILE: "_uf.story.js" = "_uf.story.js";
73
+ export const STORY_FILE: "$story.js" = "$story.js";
76
74
 
77
75
  /**
78
76
  * Which build a story file applies to.
79
77
  *
80
78
  * The router's vocabulary, exactly. `"default"` has no segment in the name.
81
79
  */
82
- export type StoryVariant = "default" | "native" | "ios" | "android" | "web" | "test";
80
+ export type StoryVariant = "default" | "native" | "ios" | "android" | "web" | "test" | "client";
83
81
 
84
- /** The variants, in the order `uf_router::ReservedVariant` declares them. */
85
- const VARIANTS: $ReadOnlyArray<StoryVariant> = ["native", "ios", "android", "web", "test"];
82
+ /** The variants, in the order `uf_router::ReservedVariant::all` lists them. */
83
+ const VARIANTS: $ReadOnlyArray<StoryVariant> = [
84
+ "native",
85
+ "ios",
86
+ "android",
87
+ "web",
88
+ "test",
89
+ "client",
90
+ ];
86
91
 
87
92
  /** Directory names the walk never descends into. */
88
93
  const IGNORED: $ReadOnlyArray<string> = [
@@ -102,10 +107,10 @@ const IGNORED: $ReadOnlyArray<string> = [
102
107
  * a caller with a path can normalise it two ways and get two answers.
103
108
  */
104
109
  export function classifyStoryFile(fileName: string): StoryVariant | null {
105
- if (!fileName.startsWith("_uf.") || !fileName.endsWith(".js")) {
110
+ if (!fileName.startsWith("$") || !fileName.endsWith(".js")) {
106
111
  return null;
107
112
  }
108
- const segments = fileName.slice("_uf.".length, -".js".length).split(".");
113
+ const segments = fileName.slice("$".length, -".js".length).split(".");
109
114
  if (segments[0] !== STORY_ROLE) {
110
115
  return null;
111
116
  }
@@ -113,7 +118,7 @@ export function classifyStoryFile(fileName: string): StoryVariant | null {
113
118
  return "default";
114
119
  }
115
120
  if (segments.length > 2) {
116
- // `_uf.story.native.test.js`: one variant, not a stack of them.
121
+ // `$story.native.test.js`: one variant, not a stack of them.
117
122
  return null;
118
123
  }
119
124
  const variant = VARIANTS.find((each) => each === segments[1]);
@@ -202,6 +207,9 @@ export async function findStoryFiles(root: string, options?: FindOptions): Promi
202
207
  */
203
208
  export async function loadStoryFile(file: string): Promise<Array<StorySet>> {
204
209
  const absolute = path.resolve(file);
210
+ // The user's story file, known only at run time; Flow types only a literal
211
+ // specifier, and `isStorySet` checks every export before it is kept.
212
+ // $FlowFixMe[unsupported-syntax]
205
213
  const module = await import(pathToFileURL(absolute).href);
206
214
  const sets: Array<StorySet> = [];
207
215
  for (const name of Object.keys(module)) {
package/index.js CHANGED
@@ -17,7 +17,7 @@
17
17
  // they are declared as data, the same declaration serves the assertion and
18
18
  // the picture.
19
19
  //
20
- // // src/components/_uf.story.js
20
+ // // src/components/$story.js
21
21
  // export const stories = defineStories({
22
22
  // title: "Button",
23
23
  // component: Button,
@@ -63,7 +63,7 @@
63
63
  // - `story.js` — **declaring**: `defineStories`, what a story inherits from
64
64
  // its set, and where the `Props` type parameter is checked and why it is
65
65
  // then erased. Pure data; declaring a story runs nothing.
66
- // - `collect.js` — **finding**: `_uf.story.js`, the repository's own reserved
66
+ // - `collect.js` — **finding**: `$story.js`, the repository's own reserved
67
67
  // name grammar, the walk, and what makes a story file valid.
68
68
  // - `render.js` — **rendering one**: the mock lifetime, the decorators, the
69
69
  // mount, and the markup. Everything here is about *time*.
@@ -84,7 +84,8 @@
84
84
  // props on the set and a partial override per story, with per-story and
85
85
  // per-set decorators, mocks and play functions; names defaulting to the
86
86
  // declaration key; stable `title--name` ids. Collecting them: the
87
- // `_uf.story.js` reserved name with the router's variant vocabulary, a
87
+ // `$story.js` reserved name — a role in `crates/uf_router/src/reserved.rs`,
88
+ // so `uf lint` accepts it — with the router's variant vocabulary, a
88
89
  // bounded walk that skips `node_modules` and symlinks, loading every story
89
90
  // set a file exports, and rejecting two stories that share an id. Rendering
90
91
  // one into `@uniflowed/react-testing`'s DOM, with the story's handlers
@@ -94,19 +95,10 @@
94
95
  // path and the original error. `renderStoryToHtml` for something that is not
95
96
  // a test. One `it` per story through `@uniflowed/story/runner`.
96
97
  //
97
- // **Experimental.** The `_uf.story.js` name itself. It follows uf's reserved
98
- // grammar — `_uf.<role>[.<variant>].js` — but `story` is not yet one of the
99
- // roles `crates/uf_router/src/reserved.rs` defines, and that file is the
100
- // grammar's single source of truth for `uf create`, the router and the
101
- // linter. Until a `story` role is added there, **`uf lint` reports
102
- // `router/reserved-files` on every `_uf.story.js`**: the name is right and
103
- // the linter has not been told. The alternative was to invent a second
104
- // convention (`*.stories.js`) that no uf tool knows about, which is worse.
105
- //
106
- // `describeStories` is experimental for a different reason, and it is a
107
- // property of `uf test` rather than of this package: discovery scans source
108
- // text for `it(` with a string-literal name, so a file whose only content is
109
- // a `describeStories` call is skipped — silently, reporting zero files and
98
+ // **Experimental.** `describeStories`, and for a reason that is a property of
99
+ // `uf test` rather than of this package: discovery scans source text for
100
+ // `it(` or `test(` with a string-literal name, so a file whose only content
101
+ // is a `describeStories` call is skipped — silently, reporting zero files and
110
102
  // exiting 0. `runner.js` documents the shape that works today and
111
103
  // `storyTest` is the escape hatch.
112
104
  //
@@ -114,11 +106,12 @@
114
106
  // no browser canvas and no static story site: this package produces the index
115
107
  // and the markup those would need, and nothing renders them for a person yet
116
108
  // beyond a string. `withBrowser` is deliberately gone rather than carried
117
- // over — `@uniflowed/browser` is still a declaration whose every function
118
- // throws, so a story that claimed to drive a real browser would be claiming a
119
- // capability that does not exist. For the same reason nothing here talks to
120
- // `@uniflowed/vrt`; `storyId` is exported so that a baseline can be filed
121
- // under the same name when it does.
109
+ // over. `@uniflowed/test/browser` now wraps `@uniflowed/test/browser`, which drives
110
+ // a real page under `uf test --browser`, but no story API is built on it yet:
111
+ // a story renders into `@uniflowed/react-testing`'s DOM, and a story test that
112
+ // wants a real browser opens one itself. Nothing here talks to
113
+ // `@uniflowed/vrt` either; `storyId` is exported so that a baseline can be
114
+ // filed under the same name when something does.
122
115
  //
123
116
  // Also absent, and each for a reason rather than by oversight: no Storybook
124
117
  // CSF compatibility, no `argTypes`, controls or knobs (a control panel needs
@@ -128,7 +121,7 @@
128
121
  // remote catalogues, and no story-level snapshot testing —
129
122
  // `@uniflowed/test`'s snapshots work on the string `renderStoryToHtml`
130
123
  // returns. Only the default variant of the reserved name is rendered:
131
- // `_uf.story.native.js` is recognised and skipped, because a React Native
124
+ // `$story.native.js` is recognised and skipped, because a React Native
132
125
  // renderer does not exist here either. Nothing renders a story through RSC or
133
126
  // server rendering; a story mounts on the client, which is what
134
127
  // `@uniflowed/react-testing` provides.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniflowed/story",
3
- "version": "0.0.0-alpha.18",
3
+ "version": "0.14.1",
4
4
  "description": "Named, rendered states of a component that a person and a test can both reach, part of the Unified Toolchain for Flow.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,7 +8,7 @@
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/ubugeeei-prod/uf.git",
11
- "directory": "packages/story"
11
+ "directory": "npm/story"
12
12
  },
13
13
  "exports": {
14
14
  ".": "./index.js",
@@ -23,10 +23,10 @@
23
23
  "!*.test.js"
24
24
  ],
25
25
  "dependencies": {
26
- "@uniflowed/mock": "0.0.0-alpha.18",
27
- "@uniflowed/react": "0.0.0-alpha.18",
28
- "@uniflowed/react-testing": "0.0.0-alpha.18",
29
- "@uniflowed/test": "0.0.0-alpha.18"
26
+ "@uniflowed/mock": "0.14.1",
27
+ "@uniflowed/react": "0.14.1",
28
+ "@uniflowed/react-testing": "0.14.1",
29
+ "@uniflowed/test": "0.14.1"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=19"
package/runner.js CHANGED
@@ -7,8 +7,9 @@
7
7
  // does not write the same mount-play-unmount block once per story.
8
8
  //
9
9
  // import { describe, it } from "@uniflowed/test";
10
+ // import { findStory } from "@uniflowed/story";
10
11
  // import { describeStories, storyTest } from "@uniflowed/story/runner";
11
- // import { stories } from "./_uf.story.js";
12
+ // import { stories } from "./$story.js";
12
13
  //
13
14
  // it("renders every Button story", storyTest(findStory(stories, "Primary")));
14
15
  // describe("Button", () => {
@@ -37,11 +38,12 @@
37
38
  // # What `uf test` can and cannot see here
38
39
  //
39
40
  // `uf test` discovers test declarations by scanning source text for `it(` and
40
- // `describe(` with a **string literal** first argument, and a file with no
41
- // such declaration is not run at all. [`describeStories`] registers its cases
42
- // from the set, so their names are not literals — which means a file whose
43
- // only content is a `describeStories` call is skipped silently, reported as
44
- // zero files and zero tests, and exits 0.
41
+ // `test(` with a **string literal** first argument, and a file with no such
42
+ // declaration is not run at all. `describe(` is read too, for the names of the
43
+ // cases inside it, but a `describe` alone does not make a file runnable.
44
+ // [`describeStories`] registers its cases from the set, so their names are not
45
+ // literals — which means a file whose only content is a `describeStories` call
46
+ // is skipped silently, reported as zero files and zero tests, and exits 0.
45
47
  //
46
48
  // So a story test file must contain at least one literal declaration, and the
47
49
  // shape above is the recommended one: a literal `describe` is not enough,