@variance-authority/core 0.13.0 → 0.15.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/CHANGELOG.md +458 -0
- package/README.md +13 -10
- package/dist/attribute/component-hash.d.ts +3 -2
- package/dist/attribute/component-hash.js +3 -2
- package/dist/attribute/component-hash.js.map +1 -1
- package/dist/attribute/composition.d.ts +16 -0
- package/dist/attribute/composition.js +4 -6
- package/dist/attribute/composition.js.map +1 -1
- package/dist/attribute/index.d.ts +3 -1
- package/dist/attribute/index.js +2 -1
- package/dist/attribute/index.js.map +1 -1
- package/dist/attribute/region.js +1 -5
- package/dist/attribute/region.js.map +1 -1
- package/dist/attribute/source.d.ts +10 -24
- package/dist/attribute/source.js +4 -38
- package/dist/attribute/source.js.map +1 -1
- package/dist/attribute/subject-pieces.d.ts +37 -0
- package/dist/attribute/subject-pieces.js +122 -0
- package/dist/attribute/subject-pieces.js.map +1 -0
- package/dist/format/capture.d.ts +25 -0
- package/dist/format/capture.js +6 -0
- package/dist/format/capture.js.map +1 -1
- package/dist/format/document.d.ts +2 -2
- package/dist/format/document.js +8 -2
- package/dist/format/document.js.map +1 -1
- package/dist/format/drive.d.ts +191 -0
- package/dist/format/drive.js +343 -0
- package/dist/format/drive.js.map +1 -0
- package/dist/format/environment.d.ts +20 -2
- package/dist/format/environment.js +7 -0
- package/dist/format/environment.js.map +1 -1
- package/dist/format/index.d.ts +6 -3
- package/dist/format/index.js +4 -2
- package/dist/format/index.js.map +1 -1
- package/dist/format/intervention.d.ts +79 -0
- package/dist/format/intervention.js.map +1 -1
- package/dist/format/origin.d.ts +32 -0
- package/dist/format/origin.js +54 -0
- package/dist/format/origin.js.map +1 -0
- package/dist/format/snapshot.d.ts +2 -0
- package/dist/format/snapshot.js.map +1 -1
- package/dist/format/stabilize.d.ts +28 -5
- package/dist/format/stabilize.js +48 -100
- package/dist/format/stabilize.js.map +1 -1
- package/dist/format/waits.d.ts +12 -0
- package/dist/format/waits.js +156 -0
- package/dist/format/waits.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/nearest.d.ts +46 -0
- package/dist/nearest.js +102 -0
- package/dist/nearest.js.map +1 -0
- package/dist/relate/before.d.ts +19 -24
- package/dist/relate/before.js +35 -37
- package/dist/relate/before.js.map +1 -1
- package/dist/relate/beyond.d.ts +59 -0
- package/dist/relate/beyond.js +98 -0
- package/dist/relate/beyond.js.map +1 -0
- package/dist/relate/carried.d.ts +122 -0
- package/dist/relate/carried.js +279 -0
- package/dist/relate/carried.js.map +1 -0
- package/dist/relate/dominate.d.ts +19 -0
- package/dist/relate/dominate.js +77 -0
- package/dist/relate/dominate.js.map +1 -0
- package/dist/relate/index.d.ts +3 -0
- package/dist/relate/index.js +3 -0
- package/dist/relate/index.js.map +1 -1
- package/dist/relate/merkle.js +2 -11
- package/dist/relate/merkle.js.map +1 -1
- package/dist/relate/narrow.d.ts +2 -2
- package/dist/relate/narrow.js +26 -73
- package/dist/relate/narrow.js.map +1 -1
- package/dist/relate/reach.d.ts +1 -1
- package/dist/relate/reach.js +10 -53
- package/dist/relate/reach.js.map +1 -1
- package/dist/relate/records.d.ts +1 -1
- package/dist/relate/records.js.map +1 -1
- package/dist/rules/index.d.ts +3 -3
- package/dist/rules/index.js +3 -3
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/normalize/cascade.d.ts +12 -0
- package/dist/rules/normalize/cascade.js +34 -16
- package/dist/rules/normalize/cascade.js.map +1 -1
- package/dist/rules/normalize/fixture.d.ts +1 -0
- package/dist/rules/normalize/fixture.js +1 -0
- package/dist/rules/normalize/fixture.js.map +1 -1
- package/dist/rules/normalize/index.d.ts +2 -2
- package/dist/rules/normalize/index.js +9 -2
- package/dist/rules/normalize/index.js.map +1 -1
- package/dist/rules/normalize/value.d.ts +13 -2
- package/dist/rules/normalize/value.js +42 -8
- package/dist/rules/normalize/value.js.map +1 -1
- package/dist/rules/normalize/wrapper.js +5 -0
- package/dist/rules/normalize/wrapper.js.map +1 -1
- package/dist/rules/ruleset.d.ts +24 -4
- package/dist/rules/ruleset.js +24 -4
- package/dist/rules/ruleset.js.map +1 -1
- package/dist/segment/index.d.ts +20 -4
- package/dist/segment/index.js +28 -7
- package/dist/segment/index.js.map +1 -1
- package/dist/shard/index.d.ts +11 -0
- package/dist/shard/index.js +11 -0
- package/dist/shard/index.js.map +1 -0
- package/dist/shard/placement.d.ts +117 -0
- package/dist/shard/placement.js +170 -0
- package/dist/shard/placement.js.map +1 -0
- package/package.json +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,463 @@
|
|
|
1
1
|
# @variance-authority/core
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8dbc834: The components a module declares are read from its code. A declaration written
|
|
8
|
+
in a block comment, a JSDoc example or a template literal no longer names a
|
|
9
|
+
component, so a commented-out `function Retired()` no longer makes its file
|
|
10
|
+
the one that declares `Retired`. A name is declared by a statement of the
|
|
11
|
+
module itself: a function, class or binding inside a function body belongs to
|
|
12
|
+
that function and no longer counts. An `export async function Page()`, an
|
|
13
|
+
abstract class, a generator, a second name in `const A = 1, B = 2`, and a name
|
|
14
|
+
that starts with a non-ASCII capital or carries a `$` are now declared too.
|
|
15
|
+
|
|
16
|
+
`variance run`, `variance collect`, the Storybook collector and the route
|
|
17
|
+
collector resolve a component to its `file:line` from the same reading, through
|
|
18
|
+
`indexDeclarations`, which `sense` now exports. What that changes in the index
|
|
19
|
+
they build from `source.dirs`:
|
|
20
|
+
|
|
21
|
+
- The line is the first line of the declaring statement. A decorated class
|
|
22
|
+
sits at its first decorator, or at its `export` when the decorator is written
|
|
23
|
+
above the `export`, and a second name in a multi-line `const` at the line of
|
|
24
|
+
the `const`.
|
|
25
|
+
- `.d.ts` files, and test, spec and story files your own `exclude` lets
|
|
26
|
+
through, declare nothing.
|
|
27
|
+
- A file the parser cannot read declares nothing, where the text scan found
|
|
28
|
+
the components in it. That covers a Flow-annotated `.js` file, and a `.vue`,
|
|
29
|
+
`.svelte` or `.mdx` file you add to a collector's `extensions`, which is
|
|
30
|
+
parsed as TSX and fails. The index built from `source.dirs` or a collector's
|
|
31
|
+
`source` does not say so: the file's components are missing, and nothing
|
|
32
|
+
names the file. Only `sense`'s source index, for a file it records, names the
|
|
33
|
+
parse error in that file's record.
|
|
34
|
+
- Building the index needs `sense`'s native addon, which ships for macOS on
|
|
35
|
+
arm64, Linux on x64 and arm64 with glibc, and Windows on x64. On any other
|
|
36
|
+
machine, including an Intel Mac, an Alpine image and Windows on arm64, a
|
|
37
|
+
configured `source.dirs` fails at its first file and names why the addon did
|
|
38
|
+
not load, where the text scan ran anywhere. The route collector depends on
|
|
39
|
+
`sense` for it.
|
|
40
|
+
|
|
41
|
+
`indexSource` is removed from `@variance-authority/core/attribute`. Build a
|
|
42
|
+
`SourceIndex` with `indexDeclarations` from `@variance-authority/sense`, or
|
|
43
|
+
write one as plain data: a map from component name to `{ file, line, via }`.
|
|
44
|
+
`SourceRef['via']` loses `'declared'`, which nothing produced, and
|
|
45
|
+
`readCapture` in `@variance-authority/unit-test` refuses a capture whose source
|
|
46
|
+
index carries it.
|
|
47
|
+
|
|
48
|
+
The source index format moves to version 18, so an index written before this
|
|
49
|
+
release is rebuilt once instead of keeping the names it read from comments.
|
|
50
|
+
- 6755276: `variance distill --file <path>` lists each module no case entered under the
|
|
51
|
+
import that made the test file load it: the topmost import every path from the
|
|
52
|
+
test file to the module runs through, with nothing behind it that a case
|
|
53
|
+
entered, often in a module the test used rather than in the test file. A module
|
|
54
|
+
two imports reach is listed as shared, with the nearest file every path to it
|
|
55
|
+
runs through; one no static import reaches, such as a dynamic import's, is
|
|
56
|
+
listed apart. The file graph is read from the checkout only when the reading
|
|
57
|
+
has a module no case entered. A module the file mocked with a factory was never
|
|
58
|
+
evaluated, and its import is not walked.
|
|
59
|
+
|
|
60
|
+
`@variance-authority/core/relate` exports `dominatorsOf`, each node's immediate
|
|
61
|
+
dominator from a root. `distillFile` takes an optional `imports` lookup and
|
|
62
|
+
sets `cause` on each module no case entered; `@variance-authority/distill`
|
|
63
|
+
exports `LoadCause`.
|
|
64
|
+
- b065364: `::before` and `::after` are read as children of the element they hang from
|
|
65
|
+
|
|
66
|
+
Restyling or rewording a `::before` or `::after` box used to repaint the page
|
|
67
|
+
and move no hash: the rules written for it were matched against the element,
|
|
68
|
+
failed, and were dropped from both the snapshot and the render document. A
|
|
69
|
+
generated box now enters the capture as a child node tagged `::before` (first)
|
|
70
|
+
or `::after` (last), carrying the rules written for it. In a browser it carries
|
|
71
|
+
its computed style, projected onto the allowlist, and the text its `content`
|
|
72
|
+
says as a `#text` child; a box whose `content` is `none` or `normal`, or whose
|
|
73
|
+
`display` is `none`, is not in the tree. Strings are joined and `attr()` reads
|
|
74
|
+
the element's attribute; a part such as `counter()` or `open-quote` is carried
|
|
75
|
+
as written.
|
|
76
|
+
|
|
77
|
+
Under JSDOM, which computes no generated content, the declared rules answer,
|
|
78
|
+
picked by the same cascade order normalization uses: a box is in the tree when
|
|
79
|
+
the winning `content` gives it one and the winning `display` is not `none`, with the rules that style it and no computed style.
|
|
80
|
+
When that `content` is strings and `attr()`, the box says its words as a
|
|
81
|
+
`#text` child, as in a browser, so rewording it moves the snapshot. When it
|
|
82
|
+
holds a `counter()`, a quote, a `var()` or a `url()`, the box has no text and
|
|
83
|
+
carries `unread` on `RawNode` and `SemanticNode`, naming the parts it could not
|
|
84
|
+
resolve; `unread` is outside every hash. JSDOM's own parser drops a `content`
|
|
85
|
+
that is a single `attr()` or `counter()` and nothing else, so no box is read
|
|
86
|
+
for one.
|
|
87
|
+
|
|
88
|
+
This is part of `RULESET_VERSION` `r2`, the same `r2` that keeps the served
|
|
89
|
+
port out of a render. A subject with a `::before` or `::after` box has a new
|
|
90
|
+
render document, so after this upgrade its image shows the box and reads
|
|
91
|
+
`changed`; `variance accept --all` and Playwright's
|
|
92
|
+
`--update-snapshots=changed` adopt it.
|
|
93
|
+
- 1a0b845: A subject is read with nothing hovered, nothing focused, motion reduced and `Date` fixed
|
|
94
|
+
|
|
95
|
+
Every layout and raster baseline made before this release is compared under a
|
|
96
|
+
moved recipe once; a semantic baseline, read in jsdom, does not move. The
|
|
97
|
+
default recipes gained tricks, so the stabilization digest every reading records
|
|
98
|
+
has changed, and each subject names that recipe as the cause of whatever it
|
|
99
|
+
reports. Review the changes and adopt them with `variance accept --all`, as for
|
|
100
|
+
any recipe change.
|
|
101
|
+
|
|
102
|
+
The standard collection recipe now also holds:
|
|
103
|
+
|
|
104
|
+
- `no-hover`: the pointer is moved to `(-1, -1)`, off the page, once per page
|
|
105
|
+
before it loads. On the `variance` fixture's page it is moved again before
|
|
106
|
+
every observation, a frame each, and not put back: a menu a script opened on
|
|
107
|
+
hover is closed for your test's next step.
|
|
108
|
+
- `no-focus`: whatever holds focus is blurred before the subject is read,
|
|
109
|
+
including an element inside a shadow root. On a page your test or agent goes
|
|
110
|
+
on using, focus goes back to that element once the reading and any screenshot
|
|
111
|
+
of it are taken, so a test types where it left off. The blur still ran the
|
|
112
|
+
page's blur handlers, and what they did, such as a validate-on-blur error,
|
|
113
|
+
stays.
|
|
114
|
+
- `reduced-motion`: the browser reports `prefers-reduced-motion: reduce`, which
|
|
115
|
+
stops the script animations of a page that honours it, and the reading
|
|
116
|
+
declares the same preference among the conditions its `@media` rules are
|
|
117
|
+
resolved against. A `prefers-reduced-motion` condition you declare through
|
|
118
|
+
`createHarness`'s `features` wins, and the browser is asked for that value;
|
|
119
|
+
a declared `no-preference` is not recorded as `reduced-motion`. Rules under
|
|
120
|
+
`@media (prefers-reduced-motion: no-preference)` are not read by default;
|
|
121
|
+
leave `reduced-motion` out to read them.
|
|
122
|
+
- `freeze-clock`: `Date` reads one instant. A script added before the page's
|
|
123
|
+
own replaces `new Date()`, `Date()` and `Date.now()`, and gives the same
|
|
124
|
+
instant to `Intl.DateTimeFormat`'s `format()` and `formatToParts()` called
|
|
125
|
+
without a date. Timers, animation frames and `performance.now()` are the
|
|
126
|
+
browser's own, so a page that settles on a timer still settles. No Playwright
|
|
127
|
+
clock is installed, so `page.clock` stays yours, and a clock your test
|
|
128
|
+
installs replaces the fixed `Date`; the reading still records `freeze-clock`,
|
|
129
|
+
so leave it out of the list when your test owns the clock. `Temporal.Now` and
|
|
130
|
+
`Date` inside a worker are not fixed. The default instant is in the past, and
|
|
131
|
+
the browser's cookie store keeps real time: a cookie your page's script sets
|
|
132
|
+
with an expiry counted from `Date.now()` is already expired and is not
|
|
133
|
+
stored, so a dismissed consent banner can come back after the next
|
|
134
|
+
navigation. Set `varianceNow` or `now` near today, or leave `freeze-clock`
|
|
135
|
+
out.
|
|
136
|
+
|
|
137
|
+
The render and layout recipes gain `no-focus` alone, because a painted document
|
|
138
|
+
still honours a collected `autofocus`. The other three have nothing to act on
|
|
139
|
+
there: the date the page printed is already text in the document, and its
|
|
140
|
+
`@media` rules were resolved when it was read. A `<script>` inside a subject
|
|
141
|
+
root is carried into the render document and runs there under the real clock.
|
|
142
|
+
Every document is painted under the render recipe, so a reading taken without
|
|
143
|
+
`no-focus` is still painted blurred.
|
|
144
|
+
|
|
145
|
+
The instant defaults to `DEFAULT_NOW`, `2024-06-15T12:00:00.000Z`, exported
|
|
146
|
+
from `@variance-authority/core/format`. `routeCollector` and
|
|
147
|
+
`storybookCollector` take a `now` option to choose another. It must be an ISO
|
|
148
|
+
8601 instant with its zone; anything else is refused before a browser opens.
|
|
149
|
+
An instant other than the default is spelled in the clock's id,
|
|
150
|
+
`freeze-clock@<instant>`, so it is part of the environment key, and a baseline taken at one instant is compared
|
|
151
|
+
with a run at another with the moved instant named as the cause.
|
|
152
|
+
|
|
153
|
+
To read a subject hovered, focused, animated or at the real time, pass your own
|
|
154
|
+
`stabilize` list without that trick: `DEFAULT_STABILIZE`, the ids of the
|
|
155
|
+
default recipe, filtered, as in
|
|
156
|
+
`DEFAULT_STABILIZE.filter((id) => id !== 'freeze-clock')`. It is exported from
|
|
157
|
+
`@variance-authority/core/format`, `@variance-authority/route-collector`,
|
|
158
|
+
`@variance-authority/storybook-collector` and
|
|
159
|
+
`@variance-authority/playwright-test`. `storybookCollector` and `observe` in
|
|
160
|
+
`@variance-authority/playwright-test` now take `stabilize`, as `routeCollector`
|
|
161
|
+
already did.
|
|
162
|
+
|
|
163
|
+
A trick needs a browser driver to apply it, and is recorded only where one did.
|
|
164
|
+
The collectors apply all four. So does the `variance` fixture in
|
|
165
|
+
`@variance-authority/playwright-test`: it sets `reduced-motion` and
|
|
166
|
+
`freeze-clock` on the `page` it hands your test, before the test body runs, and
|
|
167
|
+
takes the pointer off the page and releases focus before every observation,
|
|
168
|
+
giving focus back after it. Two new fixture options, `varianceStabilize` and
|
|
169
|
+
`varianceNow`, choose what it sets and the instant, and are set with
|
|
170
|
+
`test.use`; `varianceDriven` holds the ids it set. A fixed `Date` stops code
|
|
171
|
+
that times itself with `Date.now()`: a debounce or a throttle, such as lodash's
|
|
172
|
+
`debounce`, sees no time pass and never fires, so a test that types into a
|
|
173
|
+
debounced search box can stop getting results after this upgrade. A loop that
|
|
174
|
+
waits for `Date.now()` to pass a mark hangs the page, an animation timed by
|
|
175
|
+
`Date`, such as jQuery's `animate`, never calls its `complete`, and a client
|
|
176
|
+
that checks a token's `nbf` or `iat` against `Date.now()` can refuse a token
|
|
177
|
+
issued today. None of these failures names the clock. Leave
|
|
178
|
+
`freeze-clock` out for such a test, and for one that sets `page.clock` itself:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { DEFAULT_STABILIZE } from '@variance-authority/playwright-test';
|
|
182
|
+
|
|
183
|
+
test.use({ varianceStabilize: DEFAULT_STABILIZE.filter((id) => id !== 'freeze-clock') });
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
On a page the fixture never handed your test,
|
|
187
|
+
such as one opened from your own browser context, call the new
|
|
188
|
+
`driveBeforeTest(page)` before `page.goto()` to set the same two; `observe` and
|
|
189
|
+
`createVariance` there apply `no-hover` and `no-focus`.
|
|
190
|
+
`@variance-authority/vitest-browser` reads from inside your page and has no
|
|
191
|
+
driver; `@variance-authority/presentation` reads through the Playwright `Page`
|
|
192
|
+
you hand it, a test's or an agent's live browser, and does not drive it. Both
|
|
193
|
+
apply `no-focus` only, giving focus back once the reading is taken. A trick left out is absent from the
|
|
194
|
+
reading's ids and digest, as a trick a tier cannot reach already is.
|
|
195
|
+
`stabilizeForObservation` in `@variance-authority/dom` takes `driven`, the ids
|
|
196
|
+
your driver applied, and returns `refocus()`, which gives focus back to the
|
|
197
|
+
element the reading released. `stabilizeAsRequested` takes the recipe by id, as
|
|
198
|
+
a page agent receives it. `createHarness` in `@variance-authority/playwright`
|
|
199
|
+
takes a `stabilize` recipe to drive its page before the first navigation, and
|
|
200
|
+
reports the ids it applied as `driven`. `createObservedHarness` opens one with
|
|
201
|
+
`observeNetwork` started before its first navigation, as both collectors do.
|
|
202
|
+
|
|
203
|
+
`createPlaywrightRenderer` no longer couples `no-focus` to `waitForFonts`:
|
|
204
|
+
`waitForFonts: false` turns off the waits alone.
|
|
205
|
+
|
|
206
|
+
`@variance-authority/core/format` exports `DEFAULT_STABILIZE`, `DEFAULT_NOW`,
|
|
207
|
+
`driveBeforeLoad` and `driveBeforeRead`, which apply the driver tricks to a
|
|
208
|
+
Playwright `Page`, `forDriver`, which drops the ones a host did not apply,
|
|
209
|
+
`featuresOf`, which declares the motion preference a recipe drove, and
|
|
210
|
+
`recipeToDrive`, which resolves a `stabilize` list and checks `now` before a
|
|
211
|
+
browser opens.
|
|
212
|
+
- d432339: A page reads its clock in UTC and formats for en-US, not for the host
|
|
213
|
+
|
|
214
|
+
The harness the route and Storybook collectors open, and the pages the renderer
|
|
215
|
+
paints, took their timezone and locale from the machine. A date the page
|
|
216
|
+
formatted read one way on a laptop in Sydney and another on a runner in
|
|
217
|
+
Virginia, from one build, and the two compared against one baseline as
|
|
218
|
+
`changed`.
|
|
219
|
+
|
|
220
|
+
Both are pinned: `timezoneId` to `UTC` and `locale` to `en-US`. Declare either
|
|
221
|
+
in `variance.config.json` (`"timezoneId": "Europe/Berlin"`, `"locale":
|
|
222
|
+
"de-DE"`) and the collectors and the renderer pin that instead; an unknown
|
|
223
|
+
timezone or a malformed language tag is refused by its field.
|
|
224
|
+
`createHarness` and `createPlaywrightRenderer` take the same two options.
|
|
225
|
+
`@variance-authority/core/format` exports their type, `PageRegion`, and
|
|
226
|
+
`@variance-authority/playwright` exports `pinnedRegion`, which fills in `UTC`
|
|
227
|
+
and `en-US` for a field left out.
|
|
228
|
+
|
|
229
|
+
Both are part of the environment key, and the renderer's pair is part of
|
|
230
|
+
`RenderIdentity.rasterization`. A capture read in another timezone or language
|
|
231
|
+
is another environment, and an image painted in one is compared with a
|
|
232
|
+
baseline painted in another, with `rasterization` named as the cause of
|
|
233
|
+
whatever moved.
|
|
234
|
+
|
|
235
|
+
`@variance-authority/playwright-test` paints each test in the timezone and
|
|
236
|
+
locale its page runs in: whatever the Playwright project or `test.use` sets.
|
|
237
|
+
When neither sets a `timezoneId`, `varianceFixtures` opens the page in `UTC`
|
|
238
|
+
rather than the host's, so a laptop and a runner in UTC read it under one key;
|
|
239
|
+
Playwright Test already opens it in `en-US` when nothing sets a `locale`. That
|
|
240
|
+
reaches every test of a suite extended with `varianceFixtures`, including tests
|
|
241
|
+
that never call `variance`, so a test that asserts a date formatted in your
|
|
242
|
+
machine's timezone reads it in `UTC` after this upgrade; set `timezoneId` in
|
|
243
|
+
`use` to keep your own. A page
|
|
244
|
+
you hand to `createVariance` is read and painted in whatever its own context
|
|
245
|
+
opened it in, which is the host's timezone when that context set none: open the
|
|
246
|
+
context with `timezoneId: 'UTC'` to match. `varianceRenderer` is now a test fixture, chosen
|
|
247
|
+
by the page, and `varianceRenderers` is the worker's set of renderers, one per
|
|
248
|
+
timezone and locale; a project that overrode `varianceRenderer` at worker scope
|
|
249
|
+
overrides it at test scope instead.
|
|
250
|
+
|
|
251
|
+
**Every image painted by the Playwright renderer is compared under a moved
|
|
252
|
+
recipe once after this upgrade**, because its recipe now carries the timezone
|
|
253
|
+
and locale. Each subject names `rasterization` as the cause of what it reports,
|
|
254
|
+
and one whose pixels moved is `changed`:
|
|
255
|
+
|
|
256
|
+
- With `variance run`: the run exits `1` for each `changed` subject. Review
|
|
257
|
+
them in `variance report --format html` and adopt them with
|
|
258
|
+
`variance accept --all`. A rerun reports `unchanged`.
|
|
259
|
+
- With `@variance-authority/playwright-test`: the suite fails each `changed`
|
|
260
|
+
test, and `--update-snapshots=changed` adopts them.
|
|
261
|
+
- With `@variance-authority/vitest-browser`: `vitest --update` adopts them, as
|
|
262
|
+
`variance accept --all` does.
|
|
263
|
+
|
|
264
|
+
`@variance-authority/vitest-browser` reads no `variance.config.json` and takes no
|
|
265
|
+
timezone or locale, so it paints in `UTC` and `en-US` whatever its tab ran in,
|
|
266
|
+
and neither it nor `@variance-authority/unit-test` writes a timezone or locale
|
|
267
|
+
to the environment key.
|
|
268
|
+
- 14aefbf: Ask what one test, or one story, checks that the narrower ones inside it do
|
|
269
|
+
not.
|
|
270
|
+
|
|
271
|
+
- `variance ask test-composition --file <test> --name <words>`, and
|
|
272
|
+
`docs_test_composition`, read one recorded test as the smaller tests whose
|
|
273
|
+
regions sit inside its own, the larger tests holding it, and the regions no
|
|
274
|
+
smaller test entered: modules only it enters, and paths through a smaller
|
|
275
|
+
test's module only it takes.
|
|
276
|
+
- `variance_composition {subject}` closes with the same reading for a story:
|
|
277
|
+
the smaller stories inside it, the larger ones holding it, and what no
|
|
278
|
+
smaller story renders — components only it mounts, and components a smaller
|
|
279
|
+
story renders another way. The run carries it per subject in the report's
|
|
280
|
+
structure section; `piecesOf` in `@variance-authority/core/attribute`
|
|
281
|
+
computes it.
|
|
282
|
+
- A component mounted by more than half the suite's subjects is structure. A
|
|
283
|
+
subject that mounts a component many times now counts once, where every
|
|
284
|
+
mount used to count: a chip story is an example of the chip again when the
|
|
285
|
+
pages around it mount chips many times over.
|
|
286
|
+
- f1c1b64: A bumped package and a moved manifest are read as the repository files they
|
|
287
|
+
change, once, before anything else is asked. `beyondReach` in
|
|
288
|
+
`@variance-authority/core/relate` walks a bump through the install to the first
|
|
289
|
+
files whose runtime imports load it, and stops there: a test that entered
|
|
290
|
+
anything further along evaluated that file on the way. A moved manifest becomes
|
|
291
|
+
every file beside it. `variance select`, `variance reach` and `variance run
|
|
292
|
+
--since` hand those files on as changed whole, so the suite's `before` and the
|
|
293
|
+
record answer for them as they do for an edited file.
|
|
294
|
+
|
|
295
|
+
`before` now holds files alone. A bump of a package your setup imports runs the
|
|
296
|
+
whole suite and names the setup file, not the package. `changedBefore` takes the
|
|
297
|
+
changed files only, and `BeforeReach.packages` is gone.
|
|
298
|
+
|
|
299
|
+
The `packages` option of `narrowByJourneys`, `narrowByExecution` and
|
|
300
|
+
`selectJourneyFile` is removed: pass the files `beyondReach` returns, each with
|
|
301
|
+
no line ranges.
|
|
302
|
+
- a4f55c1: One walk carries a change to every file that loads it
|
|
303
|
+
|
|
304
|
+
`@variance-authority/core/relate` exports `carried`, the walk against the
|
|
305
|
+
arrows that `dependentsOf`, its narrowed form and test selection's two importer
|
|
306
|
+
walks now all run. You pass seeds, each with what its change carries — the
|
|
307
|
+
whole file, or the names that moved — and a step that says what one importer
|
|
308
|
+
does with what reached it. The walk owns the worklist, enters each file once
|
|
309
|
+
per name, ends on any cycle, and treats a step with no answer as the whole
|
|
310
|
+
file. A walk you write over the graph gets the same answers on cycles,
|
|
311
|
+
self-imports and re-exports as the ones the selector runs, with no loop of your
|
|
312
|
+
own.
|
|
313
|
+
- bd98c3a: `--shard` in Vitest and Jest is placed by recorded time, and `variance shards` says how many to start
|
|
314
|
+
|
|
315
|
+
A Vitest or Jest config wrapped by `withTestSelection` now places the files of
|
|
316
|
+
`--shard k/n` by the time each took on the last recorded run, rather than
|
|
317
|
+
leaving the runner to divide them by count. Each shard computes the whole
|
|
318
|
+
placement from the same record and keeps its own part: files go longest first
|
|
319
|
+
to the shard with the least time so far, and a file the record has not seen is
|
|
320
|
+
priced at the median of the ones it has. The run says on stderr which record
|
|
321
|
+
placed it and what each shard was given:
|
|
322
|
+
|
|
323
|
+
```text
|
|
324
|
+
variance-authority: shard 1/2 by the times recorded at 8e3b0f0e9028: 1 of 5 files, 9.0 s (shards 400 ms to 9.0 s)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
When the times cannot be read, the runner's own split runs instead, and the
|
|
328
|
+
line says why. A failed read never fails the run. A Jest config that names no
|
|
329
|
+
`testSequencer` finds Jest's own through Jest, so a layout that does not hoist
|
|
330
|
+
`@jest/test-sequencer`, as pnpm does not, runs as before. Under
|
|
331
|
+
`VARIANCE_AUTHORITY_SINCE`, only the files the selection keeps are placed.
|
|
332
|
+
|
|
333
|
+
`variance shards --setup <seconds>` prints how many shards a recorded suite is
|
|
334
|
+
worth, and with `--format json` a `matrix` of `k/n` strings for a CI job to
|
|
335
|
+
start. Every shard is charged its setup, so a shard is added only when it
|
|
336
|
+
shortens the wait by more than it spends, and the count stops at the slowest
|
|
337
|
+
test file, which the answer names with its slowest case. `--budget` asks for
|
|
338
|
+
the fewest shards that finish within it, `--workers` divides a shard's share
|
|
339
|
+
among its runner's workers, and `--since` leaves out what the change lets the
|
|
340
|
+
run skip, so a change that reaches no test answers `0 shards`. `--collected
|
|
341
|
+
<file>`, the runner's list of test files, counts a test file the record has not
|
|
342
|
+
seen, priced at the median.
|
|
343
|
+
|
|
344
|
+
It always answers, and names what it could not weigh, in `notes` under
|
|
345
|
+
`--format json`. Without `--setup` the count weighs no setup. A budget no
|
|
346
|
+
longer than the setup is out of reach at any count, and the answer is the count
|
|
347
|
+
that finishes soonest, with `why` set to `setup over budget`. `--since` without
|
|
348
|
+
`--collected` counts only recorded files and never answers fewer than one
|
|
349
|
+
shard, and `--at-distance` without `--since` counts the whole suite. With
|
|
350
|
+
nothing recorded it answers one shard until `--unrecorded <n>` names the count
|
|
351
|
+
to start.
|
|
352
|
+
|
|
353
|
+
`@variance-authority/core/shard` is the placement and the count, which
|
|
354
|
+
`variance run --shard` places stories and routes by too.
|
|
355
|
+
`@variance-authority/sense` exports `recordedTimes` and `fileCases`, and
|
|
356
|
+
`@variance-authority/cli` exports `suiteTimes`.
|
|
357
|
+
- af6b022: `before` moves out of `source` and is declared where the suite is. The top-level
|
|
358
|
+
`before` is what every suite rests on — a CI workflow, a `.nvmrc` — and a
|
|
359
|
+
suite's own `before`, beside its `kind`, is its runner config and its setup
|
|
360
|
+
files, each listed, since a config names its setup as a string and a string is
|
|
361
|
+
not an import. `variance select` now reads both for the suite it reads: each
|
|
362
|
+
entry is walked down the file graph, and a change to any file it reaches, a
|
|
363
|
+
`package.json` whose `exports`, `main` or `type` moved over one of those files,
|
|
364
|
+
or a bump of a package those files import, runs the whole suite and names what
|
|
365
|
+
moved. A directory in either list is walked from every file under it. `select
|
|
366
|
+
--execution` reads the same lists for the suite `--suite` names, the only one
|
|
367
|
+
declared, or every declared suite when none is named; beside a snapshot
|
|
368
|
+
`--execution`, `--suite` is refused, since both name the record. A manifest move
|
|
369
|
+
is read when the diff leaves every lockfile alone. A
|
|
370
|
+
config below the repository root inherits the root's `before`, as it inherits
|
|
371
|
+
its suites. A suite that declares nothing has nothing before its reach, and
|
|
372
|
+
`select` says so.
|
|
373
|
+
`variance run --since` reads the top-level list as it read `source.before`.
|
|
374
|
+
|
|
375
|
+
`source.before` is refused: move it to the top level.
|
|
376
|
+
- 754d04c: The port a build is served on is no part of a render
|
|
377
|
+
|
|
378
|
+
The route and Storybook collectors serve a build on a free port, and that port
|
|
379
|
+
reached three identities. An asset the page loaded was keyed by its absolute
|
|
380
|
+
URL, a `url()` in a computed style was the absolute URL the engine resolved,
|
|
381
|
+
and the document digest read both. One build served twice therefore read as two
|
|
382
|
+
environment keys and two documents: each run missed the render cache, painted
|
|
383
|
+
again, and read its document as changed against its own baseline.
|
|
384
|
+
|
|
385
|
+
An asset the page's own origin serves is keyed by its path, and the page's
|
|
386
|
+
origin is dropped from a `url()` before it is hashed. An asset another origin
|
|
387
|
+
serves keeps its absolute URL. A capture carries the page's `baseUrl` so core
|
|
388
|
+
knows which origin is the page's, and the renderer resolves a path-keyed asset
|
|
389
|
+
against the document's `baseUrl`. `documentDigest` reads the page's own base
|
|
390
|
+
and resources as paths, so one build served on two ports addresses one render
|
|
391
|
+
and the render cache hits; the document keeps them absolute for the renderer,
|
|
392
|
+
and a resource another origin serves stays absolute in the digest too.
|
|
393
|
+
|
|
394
|
+
A `@variance-authority/unit-test` capture keys its assets the same way, so a
|
|
395
|
+
`@variance-authority/vitest-browser` observation, which captures through it on
|
|
396
|
+
the port the runner had free, keeps the port out of its environment key.
|
|
397
|
+
`readCapture` reads back a capture whose asset is keyed by path, resolving the
|
|
398
|
+
key against the document's `baseUrl` as the renderer does; `assetUrl` in
|
|
399
|
+
`@variance-authority/core/format` is that one resolution.
|
|
400
|
+
|
|
401
|
+
**The document of every page served over `http` moves once after this
|
|
402
|
+
upgrade.** `documentDigest` now reads a document's base and resources without
|
|
403
|
+
the page's origin, so the digest of every document served over `http` or
|
|
404
|
+
`https` changes: every route, every story, every Vitest browser page, and every
|
|
405
|
+
Playwright page opened by URL. A moved document misses the render cache once,
|
|
406
|
+
and its image is still compared with the baseline: a page whose pixels did not
|
|
407
|
+
move reads `unchanged`, and one whose pixels did reads `changed`, which
|
|
408
|
+
`variance accept --all` and Playwright's `--update-snapshots=changed` adopt.
|
|
409
|
+
`RULESET_VERSION` moves from `r1` to `r2`. The ruleset is part of a snapshot's
|
|
410
|
+
environment key, not of the render recipe, so no image names it as a moved
|
|
411
|
+
identity; a snapshot diffed against one recorded under `r1` names the ruleset as its
|
|
412
|
+
environment root.
|
|
413
|
+
- 8187fb1: `didYouMean` and `nearest` are exported from `@variance-authority/core` and no
|
|
414
|
+
longer from `@variance-authority/mcp/tools`. Import them from
|
|
415
|
+
`@variance-authority/core`. A mistyped command or flag is refused, with the
|
|
416
|
+
nearest right one, without loading the MCP server's tools.
|
|
417
|
+
- ca7813e: `intersectionArea` is exported from `@variance-authority/core/format`
|
|
418
|
+
|
|
419
|
+
The area two `Rect`s share, zero when they only touch or do not meet. It sits
|
|
420
|
+
beside `Rect`, where the attribution already used it, so the review service can
|
|
421
|
+
ask whether two builds changed the same place.
|
|
422
|
+
|
|
423
|
+
### Patch Changes
|
|
424
|
+
|
|
425
|
+
- 85e1a40: `wait-for-fonts` gives up after 15 seconds and names the font
|
|
426
|
+
|
|
427
|
+
A web font whose request never answered kept `document.fonts.ready` pending, and
|
|
428
|
+
the capture waited forever. `wait-for-fonts` now waits at most 15 seconds, the
|
|
429
|
+
same bound as `wait-for-images`, then fails naming every font still loading:
|
|
430
|
+
its family, and the `src` that requested it when the stylesheet declaring it is
|
|
431
|
+
same-origin. A face declared in a cross-origin stylesheet is named by family
|
|
432
|
+
alone, because the page cannot read that sheet's rules.
|
|
433
|
+
- cb8a52f: A source index another release wrote is no longer reported as damaged
|
|
434
|
+
|
|
435
|
+
After an upgrade, the first command that read the source index said it was
|
|
436
|
+
damaged and read only up to its first bad segment, and in CI it refused with
|
|
437
|
+
the same words. The segments were whole: an earlier release had written them in
|
|
438
|
+
another format version. Now the reader names both versions, for example that
|
|
439
|
+
the index was written in format version 15 and this release reads version 18.
|
|
440
|
+
On a workstation it rebuilds the index and says so, and `variance index` prints
|
|
441
|
+
`source index rebuilt over one written in format version 15`. In CI the refusal
|
|
442
|
+
names both versions and asks you to run `variance index` with this release
|
|
443
|
+
before the command that reads it. `ask orient --files` over such an index says
|
|
444
|
+
which version wrote it and that `variance index` rebuilds it.
|
|
445
|
+
- ef496ad: READMEs name what the program does
|
|
446
|
+
|
|
447
|
+
The package READMEs, and the `@variance-authority/vantage` and
|
|
448
|
+
`@variance-authority/playwright-test` descriptions, no longer write a test, a
|
|
449
|
+
record or a run as something that says, asks or knows. Each sentence names what
|
|
450
|
+
the program does: a command prints, a record holds a field, a test runs or
|
|
451
|
+
covers. Where a value is filled in from configuration or a default rather than
|
|
452
|
+
recorded, the README says so. Four renamed headings change their anchors:
|
|
453
|
+
`help`'s "Where the declaration is undocumented", `playwright`'s "Where a
|
|
454
|
+
component is declared, read from the engine", `storybook`'s "What a pass sends
|
|
455
|
+
the preview" and `sense`'s "Correct what a file's text declares it imports".
|
|
456
|
+
|
|
457
|
+
## 0.14.0
|
|
458
|
+
|
|
459
|
+
Lockstep release — nothing in this package changed. Every `@variance-authority/*` package shares one version.
|
|
460
|
+
|
|
3
461
|
## 0.13.0
|
|
4
462
|
|
|
5
463
|
Lockstep release — nothing in this package changed. Every `@variance-authority/*` package shares one version.
|
package/README.md
CHANGED
|
@@ -158,12 +158,14 @@ specifier exports only the capture artifact.
|
|
|
158
158
|
| `core/relate` | what rests on what: a file graph in adjacency form, the components a change reaches, and a closure digest over each one |
|
|
159
159
|
| `core/segment` | columnar bytes: named columns, interned strings, and the validation a decode performs before it believes a file |
|
|
160
160
|
| `core/share` | leaving those bytes where another machine finds them, as the latest of a mainline or a branch |
|
|
161
|
+
| `core/shard` | which shard runs which group of a suite, by recorded time, and how many shards it is worth: `shardCount` weighs each shard's `setup` against the time it saves, within a `budget` or up to a `max`, with `workers` running a shard's groups at once |
|
|
161
162
|
|
|
162
163
|
Five of those are the order an answer travels through: `format`, `rules`,
|
|
163
|
-
`compare`, `attribute`, `judge`. `plan` and `relate`
|
|
164
|
+
`compare`, `attribute`, `judge`. `plan` and `relate` run before anything
|
|
164
165
|
is captured — one decides which baselines a run can use, the other which
|
|
165
166
|
subjects are worth reading. `segment` and `share` are how a derived answer
|
|
166
|
-
is written down and handed to another machine.
|
|
167
|
+
is written down and handed to another machine. `shard` splits one run across
|
|
168
|
+
several.
|
|
167
169
|
|
|
168
170
|
## The four words the output uses
|
|
169
171
|
|
|
@@ -172,9 +174,9 @@ structure and declared style; `chromium` adds layout and pixels. A profile that
|
|
|
172
174
|
cannot measure a box cannot decide pixels either.
|
|
173
175
|
|
|
174
176
|
An **identity** is the content hash that addresses a result, derived from the
|
|
175
|
-
whole plan — ruleset version, viewport, policy, interventions. Two runs
|
|
176
|
-
|
|
177
|
-
changes
|
|
177
|
+
whole plan — ruleset version, viewport, policy, interventions. Two runs share
|
|
178
|
+
a result only when their identities match, so changing any part of the plan
|
|
179
|
+
changes the address its result is stored under.
|
|
178
180
|
|
|
179
181
|
A **band** is the kind of change a delta is, and which band a delta lands in
|
|
180
182
|
decides how loudly it is reported. Change frequency and change importance run
|
|
@@ -255,6 +257,7 @@ observation profiles differ.
|
|
|
255
257
|
| `fingerprintOfMask` | `grid` controls the shape sample and `coverage` the minimum occupied share |
|
|
256
258
|
| `compareLocales` / ignore validation | `slack` permits a declared locale distance; `sites` supplies resolved ignore locations and `now` evaluates expiry |
|
|
257
259
|
| `buildDocket` / dependency reach | `sampleSize` limits review examples; `through` selects graph edge kinds, `avoid` names nodes a walk never covers, and `shadows` lists per file the modules its run never loads, so `affectedBy` leaves out a file every trail to which crosses one of its own shadows; `depends` adds the install — which package rests on which — so a dependency bump is a seed like any other and affects only the files that import it; `uses` names what each import binds and each re-export republishes, and `moved` lists per seed the exports its change changed, so `affectedBy` covers only the files that import one of them |
|
|
260
|
+
| `carried` | the walk under every reach question, with your own step per importer: `through` and `avoid` as above; `mask` and `via` take arrays you already hold, so a caller that walks again writes into the same marks and trail; `seedsHold` set to `false` lets a seed take back what a cycle carries to it, for a file that reads its own names through an importer |
|
|
258
261
|
| `beforeReach` | `sensed` names the directories the scan already answers for, and the descent from a declared entry point stops at the first file under one of them rather than dragging the repository's own source in behind the harness |
|
|
259
262
|
| `sharedClosures` | `floor` drops a shared subtree below a node count |
|
|
260
263
|
| `lexiconOf` | `examples`, `declaredIn` and `regions` supply what the instances cannot: which components a subject is the example of, the files declaring each component, and the regions its journey covered |
|
|
@@ -263,8 +266,8 @@ observation profiles differ.
|
|
|
263
266
|
|
|
264
267
|
## Which input changed
|
|
265
268
|
|
|
266
|
-
`compare`
|
|
267
|
-
them, `partingOf`
|
|
269
|
+
`compare` finds what changed. `judge` decides whether anyone should mind.
|
|
270
|
+
Between them, `partingOf` finds which input changed.
|
|
268
271
|
|
|
269
272
|
Given two snapshots with holdings — the inputs a collector recorded at each
|
|
270
273
|
component boundary — it walks the boundaries for the shallowest one whose inputs
|
|
@@ -293,7 +296,7 @@ run that recorded no component provenance gets `unread` instead.
|
|
|
293
296
|
What a suite is made of is a fact about a commit rather than about a run.
|
|
294
297
|
`core/segment` writes such a thing as columns — equal facts encode to equal
|
|
295
298
|
bytes, which is what lets a transport skip an upload — and `core/share` keeps
|
|
296
|
-
the latest of those bytes on a line. A checkout
|
|
299
|
+
the latest of those bytes on a line. A checkout looks up *what is mainline now*,
|
|
297
300
|
and a line answers that: one mainline or one branch, holding the latest entry
|
|
298
301
|
of each kind — the run report, one record per suite — and the images those
|
|
299
302
|
entries name, by digest.
|
|
@@ -329,7 +332,7 @@ appended to. `headers` is sent on every request, which is where a bucket's
|
|
|
329
332
|
`Authorization` or a deployment's token goes. `method` is the verb a write uses
|
|
330
333
|
— `PUT` for a bucket, `POST` for a deployment that routes on it. `timeoutMs`
|
|
331
334
|
is how long one request may take, reading its body included, before the store
|
|
332
|
-
is `unreachable` with a message
|
|
335
|
+
is `unreachable` with a message naming the timeout; it is 60 000 when you
|
|
333
336
|
leave it out. A presigned base needs only the endpoint. `memoryLineCell()`
|
|
334
337
|
holds a line in this process, for tests and for a run that shares with itself.
|
|
335
338
|
|
|
@@ -343,7 +346,7 @@ A publish replaces only the entries it carries, so suites from different jobs
|
|
|
343
346
|
land on one line. On a mainline, a held entry whose commit descends from yours
|
|
344
347
|
stays: that is an older run finishing last. A held entry in a newer format stays
|
|
345
348
|
on any line. A line reports why it missed — `absent`, `newer`, `refused`,
|
|
346
|
-
`unreachable` or `unreadable` — because each
|
|
349
|
+
`unreachable` or `unreadable` — because each needs a different action from you.
|
|
347
350
|
Everything a line holds can be derived again, so a miss costs you the
|
|
348
351
|
derivation. [Sharing an evaluation](https://variance-authority.dev/docs/sharing)
|
|
349
352
|
is the operator's side of it.
|
|
@@ -103,8 +103,9 @@ export declare function causesBetween(before: readonly ComponentHash[], after: r
|
|
|
103
103
|
* A missing `geometry` on either side is the profile saying it has no layout
|
|
104
104
|
* engine, and is not a difference. Treating absent as a change would report
|
|
105
105
|
* every component as having moved the moment a jsdom baseline met a Chromium
|
|
106
|
-
* run — which the environment key already
|
|
107
|
-
* would be a second, wronger answer to a question already
|
|
106
|
+
* run — which the environment key already names as the cause of that
|
|
107
|
+
* comparison, so this would be a second, wronger answer to a question already
|
|
108
|
+
* settled.
|
|
108
109
|
*/
|
|
109
110
|
export declare function movedBands(before: BandDigests, after: BandDigests): readonly Band[];
|
|
110
111
|
/**
|
|
@@ -198,8 +198,9 @@ function ownContentMoved(before, after) {
|
|
|
198
198
|
* A missing `geometry` on either side is the profile saying it has no layout
|
|
199
199
|
* engine, and is not a difference. Treating absent as a change would report
|
|
200
200
|
* every component as having moved the moment a jsdom baseline met a Chromium
|
|
201
|
-
* run — which the environment key already
|
|
202
|
-
* would be a second, wronger answer to a question already
|
|
201
|
+
* run — which the environment key already names as the cause of that
|
|
202
|
+
* comparison, so this would be a second, wronger answer to a question already
|
|
203
|
+
* settled.
|
|
203
204
|
*/
|
|
204
205
|
export function movedBands(before, after) {
|
|
205
206
|
const moved = new Set();
|