@excom/quark 0.1.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/.rush/temp/chunked-rush-logs/quark.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/quark.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +3 -0
- package/config/rig.json +6 -0
- package/index.ts +4 -0
- package/language.ts +7 -0
- package/package.json +45 -0
- package/rush-logs/quark.apply-exports.cache.log +1 -0
- package/rush-logs/quark.apply-exports.log +1 -0
- package/rush-logs/quark.build_package-metas.cache.log +1 -0
- package/rush-logs/quark.build_package-metas.log +1 -0
- package/src/actions.ts +445 -0
- package/src/ast.ts +187 -0
- package/src/bindings.ts +115 -0
- package/src/builtin-modules.ts +401 -0
- package/src/constants.ts +47 -0
- package/src/cycle-check.ts +121 -0
- package/src/devtools-hook.ts +303 -0
- package/src/element-api.ts +109 -0
- package/src/evaluator.ts +317 -0
- package/src/form-controls.ts +69 -0
- package/src/language-tables.ts +137 -0
- package/src/language.ts +1225 -0
- package/src/observer.ts +110 -0
- package/src/paint.ts +305 -0
- package/src/properties.ts +818 -0
- package/src/props.ts +94 -0
- package/src/quark-element.ts +7 -0
- package/src/quark-internal.ts +365 -0
- package/src/quark.ts +817 -0
- package/src/resolvers.ts +867 -0
- package/src/rule.ts +800 -0
- package/src/scope-id.ts +47 -0
- package/src/selector-utils.ts +430 -0
- package/src/settle.ts +117 -0
- package/src/types.ts +155 -0
- package/src/utils.ts +79 -0
- package/src/variables.ts +359 -0
- package/support/demos/css-variables.html +18 -0
- package/support/demos/dispatch.html +20 -0
- package/support/demos/events.html +9 -0
- package/support/demos/iterate.html +13 -0
- package/support/demos/js-api.html +17 -0
- package/support/demos/provider-list.html +16 -0
- package/support/demos/toggle-content.html +14 -0
- package/support/demos/unset.html +12 -0
- package/support/demos/view-transition.html +36 -0
- package/support/docs/ATTRIBUTES.md +37 -0
- package/support/docs/AT_RULES.md +45 -0
- package/support/docs/BUILTINS.md +57 -0
- package/support/docs/CONTENT.md +40 -0
- package/support/docs/CSS_VARIABLES.md +16 -0
- package/support/docs/DECLARATIONS.md +26 -0
- package/support/docs/DELAY.md +37 -0
- package/support/docs/DIAGNOSTICS.md +32 -0
- package/support/docs/DISPATCH.md +81 -0
- package/support/docs/ELEMENT_PROPERTIES.md +34 -0
- package/support/docs/EXPRESSIONS.md +31 -0
- package/support/docs/INTERNAL.md +8 -0
- package/support/docs/JS_API.md +53 -0
- package/support/docs/JS_WRITES.md +36 -0
- package/support/docs/LIMITATIONS.md +26 -0
- package/support/docs/LOOP_GUARD.md +30 -0
- package/support/docs/METHODS.md +43 -0
- package/support/docs/MODULES.md +111 -0
- package/support/docs/NO_REVERSION.md +18 -0
- package/support/docs/ON.md +87 -0
- package/support/docs/REACTIVITY.md +24 -0
- package/support/docs/README.md +143 -0
- package/support/docs/SELECTORS.md +87 -0
- package/support/docs/SHEETS.md +67 -0
- package/support/docs/SYNTAX.md +36 -0
- package/support/docs/USE.md +24 -0
- package/support/docs/VALUES.md +27 -0
- package/support/docs/VARIABLES.md +47 -0
- package/support/docs/VIEW_TRANSITION.md +64 -0
- package/support/docs-sections.json +65 -0
- package/support/package-meta.json +208 -0
- package/support/scripts/build-language-docs.mjs +196 -0
- package/support/tests/__snapshots__/css-variables.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/dispatch.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/events.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/iterate.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/js-api.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/provider-list.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/quark-features.test.ts.snap +1079 -0
- package/support/tests/__snapshots__/quark-with-providers.test.ts.snap +89 -0
- package/support/tests/__snapshots__/quark.test.ts.snap +221 -0
- package/support/tests/__snapshots__/selectors.test.ts.snap +133 -0
- package/support/tests/__snapshots__/toggle-content.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/unset.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/view-transition.view.test.ts.snap +23 -0
- package/support/tests/actions.test.ts +437 -0
- package/support/tests/builtin-modules.test.ts +337 -0
- package/support/tests/builtins.test.ts +189 -0
- package/support/tests/css-variables.view.test.ts +35 -0
- package/support/tests/delay.test.ts +244 -0
- package/support/tests/devtools-hook.test.ts +496 -0
- package/support/tests/diagnostics.test.ts +224 -0
- package/support/tests/dispatch.view.test.ts +43 -0
- package/support/tests/element-api.test.ts +202 -0
- package/support/tests/evaluator.test.ts +237 -0
- package/support/tests/events.view.test.ts +40 -0
- package/support/tests/form-controls.test.ts +452 -0
- package/support/tests/helpers.ts +354 -0
- package/support/tests/internals.test.ts +568 -0
- package/support/tests/iterate.view.test.ts +40 -0
- package/support/tests/js-api.view.test.ts +45 -0
- package/support/tests/language-docs.test.ts +147 -0
- package/support/tests/lifecycle.test.ts +440 -0
- package/support/tests/loop-guard.test.ts +323 -0
- package/support/tests/provider-list.view.test.ts +47 -0
- package/support/tests/quark-features.test.ts +2887 -0
- package/support/tests/quark-with-providers.test.ts +169 -0
- package/support/tests/quark.test.ts +359 -0
- package/support/tests/resolvers.test.ts +346 -0
- package/support/tests/selector-analysis.test.ts +281 -0
- package/support/tests/selectors.test.ts +670 -0
- package/support/tests/toggle-content.view.test.ts +40 -0
- package/support/tests/unset.view.test.ts +42 -0
- package/support/tests/view-helpers.ts +81 -0
- package/support/tests/view-transition.test.ts +1069 -0
- package/support/tests/view-transition.view.test.ts +76 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { Quark } from "../../index";
|
|
2
|
+
import * as paint from "../../src/paint";
|
|
3
|
+
import { QuarkRegistry } from "../../src/quark";
|
|
4
|
+
import { isQuarkBusy } from "../../src/settle";
|
|
5
|
+
import { Attribute, Listener, Variable } from "../../src/properties";
|
|
6
|
+
import { QuarkInternal } from "../../src/quark-internal";
|
|
7
|
+
import type { QuarkOptions } from "../../src/types";
|
|
8
|
+
import {
|
|
9
|
+
expect,
|
|
10
|
+
fixture,
|
|
11
|
+
vi,
|
|
12
|
+
wait,
|
|
13
|
+
} from "@excom/heft-rig/profiles/default/config/test-utils";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Sheet + targets must share a parent (Quark host = sheetElement.parent).
|
|
17
|
+
* Sheets are host-scoped by default (quark-sheet's default). Pass
|
|
18
|
+
* `{ isScoped: false }` for global (root-context) sheets.
|
|
19
|
+
*/
|
|
20
|
+
export const createSheet = (
|
|
21
|
+
bodyHtml: string,
|
|
22
|
+
src: string,
|
|
23
|
+
modules?: Record<string, unknown>,
|
|
24
|
+
options?: QuarkOptions
|
|
25
|
+
) => {
|
|
26
|
+
const root = fixture<HTMLElement>(
|
|
27
|
+
`<section><div id="sheet"></div>${bodyHtml}</section>`
|
|
28
|
+
);
|
|
29
|
+
const sheetElement = root.querySelector("#sheet") as HTMLElement;
|
|
30
|
+
const quark = new Quark({ src, options: { isScoped: true, ...options } });
|
|
31
|
+
const register = () =>
|
|
32
|
+
quark.register({
|
|
33
|
+
sheetElement,
|
|
34
|
+
...(modules ? { modules: { dfault: modules } } : {}),
|
|
35
|
+
});
|
|
36
|
+
return { root, quark, sheetElement, register };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const mount = (
|
|
40
|
+
bodyHtml: string,
|
|
41
|
+
src: string,
|
|
42
|
+
modules?: Record<string, unknown>,
|
|
43
|
+
options?: QuarkOptions
|
|
44
|
+
) => {
|
|
45
|
+
const sheet = createSheet(bodyHtml, src, modules, options);
|
|
46
|
+
sheet.register();
|
|
47
|
+
return sheet;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Unregister every sheet still registered. Sheets listen for provision /
|
|
52
|
+
* binding events on the document root, so one left registered by an
|
|
53
|
+
* earlier test keeps matching (until its host is GC'd, which makes
|
|
54
|
+
* complexity counts depend on GC timing). Call from `afterEach`.
|
|
55
|
+
*/
|
|
56
|
+
export const unregisterAll = () => {
|
|
57
|
+
QuarkRegistry.sheets.forEach((ref) => {
|
|
58
|
+
const sheet = ref.deref();
|
|
59
|
+
if (sheet?.isRegistered) sheet.unregister();
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Quark settles across MutationObserver + double setTimeout(0) rule
|
|
65
|
+
* runs + paint commit. Four macrotasks cover most observer-driven
|
|
66
|
+
* updates. A paint queued at the last tick (first-run `iterate()` whose
|
|
67
|
+
* rows then bind their content) needs one more, so keep waiting while
|
|
68
|
+
* the engine reports work (`isQuarkBusy`: queued elements, a run in
|
|
69
|
+
* flight, paints waiting to commit, async content or `@use` modules
|
|
70
|
+
* pending).
|
|
71
|
+
*/
|
|
72
|
+
export const flush = async () => {
|
|
73
|
+
await wait(0);
|
|
74
|
+
await wait(0);
|
|
75
|
+
await wait(0);
|
|
76
|
+
await wait(0);
|
|
77
|
+
for (let i = 0; i < 16 && isQuarkBusy(); i++) await wait(0);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type ComplexityBudget = {
|
|
81
|
+
quarkRuns: number;
|
|
82
|
+
ruleRuns: number;
|
|
83
|
+
variableRuns: number;
|
|
84
|
+
attributeRuns: number;
|
|
85
|
+
listenerRuns: number;
|
|
86
|
+
setVar: number;
|
|
87
|
+
getVar: number;
|
|
88
|
+
schedulePaint: number;
|
|
89
|
+
querySelectorAll: number;
|
|
90
|
+
/**
|
|
91
|
+
* Weighted cost of rule fan-out queries
|
|
92
|
+
* (`ancestor.querySelectorAll(sel)` in Rule._run): each call adds the
|
|
93
|
+
* number of elements in the scanned subtree. Lower means the
|
|
94
|
+
* nearest-common-ancestor logic rooted queries deeper.
|
|
95
|
+
*/
|
|
96
|
+
queryScopeCost: number;
|
|
97
|
+
matches: number;
|
|
98
|
+
closest: number;
|
|
99
|
+
parentElement: number;
|
|
100
|
+
setAttribute: number;
|
|
101
|
+
removeAttribute: number;
|
|
102
|
+
textContent: number;
|
|
103
|
+
importNode: number;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Count Quark work units (not wall-clock). Snapshots are the regression
|
|
108
|
+
* baseline.
|
|
109
|
+
*
|
|
110
|
+
* Start measuring before the work under test; call `take()` after it
|
|
111
|
+
* settles. For observer-driven cases, apply triggering DOM writes
|
|
112
|
+
* *before* measuring so harness mutations are not counted.
|
|
113
|
+
*
|
|
114
|
+
* Do not spy `queueRunRules` after `register()`: MutationObserver
|
|
115
|
+
* closes over the original function at observe time.
|
|
116
|
+
*/
|
|
117
|
+
/** Elements in `el`'s subtree, the scan breadth of a query rooted there. */
|
|
118
|
+
const countDescendants = (el: Element): number => {
|
|
119
|
+
let count = 0;
|
|
120
|
+
for (const child of el.children) count += 1 + countDescendants(child);
|
|
121
|
+
return count;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export const measureComplexity = (quark: Quark) => {
|
|
125
|
+
const ruleRunsBefore = quark.rules.reduce((n, r) => n + r.numberOfRuns, 0);
|
|
126
|
+
/*
|
|
127
|
+
* Fan-out queries are identifiable by selector: scoped rules query
|
|
128
|
+
* with their `[q-scope="<id>"]`-prefixed selector (id exists only
|
|
129
|
+
* after register, so collect lazily in take()); unscoped with the
|
|
130
|
+
* bare one.
|
|
131
|
+
*/
|
|
132
|
+
const ruleSelectors = () =>
|
|
133
|
+
new Set(quark.rules.flatMap((r) => [r.matchSelector, r.scopedSelector()]));
|
|
134
|
+
|
|
135
|
+
const runSpy = vi.spyOn(quark, "run");
|
|
136
|
+
// Property executions by kind, the primary "work done" regression signal.
|
|
137
|
+
const variableRunSpy = vi.spyOn(Variable.prototype, "_run");
|
|
138
|
+
const attributeRunSpy = vi.spyOn(Attribute.prototype, "_run");
|
|
139
|
+
const listenerRunSpy = vi.spyOn(Listener.prototype, "_run");
|
|
140
|
+
// Element-state traffic (_q_): variable binding writes and reads.
|
|
141
|
+
const setVarSpy = vi.spyOn(QuarkInternal.prototype, "setVar");
|
|
142
|
+
const getVarSpy = vi.spyOn(QuarkInternal.prototype, "getVar");
|
|
143
|
+
const paintSpy = vi.spyOn(paint, "schedulePaint");
|
|
144
|
+
const qsaSpy = vi.spyOn(Element.prototype, "querySelectorAll");
|
|
145
|
+
const matchesSpy = vi.spyOn(Element.prototype, "matches");
|
|
146
|
+
const closestSpy = vi.spyOn(Element.prototype, "closest");
|
|
147
|
+
// upward traversal cost, binding resolution walks ancestors via
|
|
148
|
+
// `el.parentElement` (findBindingOwner in src/bindings.ts)
|
|
149
|
+
const parentElementSpy = vi.spyOn(Node.prototype, "parentElement", "get");
|
|
150
|
+
const setAttrSpy = vi.spyOn(Element.prototype, "setAttribute");
|
|
151
|
+
const removeAttrSpy = vi.spyOn(Element.prototype, "removeAttribute");
|
|
152
|
+
const textContentSpy = vi.spyOn(Element.prototype, "textContent", "set");
|
|
153
|
+
const importNodeSpy = vi.spyOn(document, "importNode");
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
take: (): ComplexityBudget => ({
|
|
157
|
+
quarkRuns: runSpy.mock.calls.length,
|
|
158
|
+
ruleRuns:
|
|
159
|
+
quark.rules.reduce((n, r) => n + r.numberOfRuns, 0) - ruleRunsBefore,
|
|
160
|
+
variableRuns: variableRunSpy.mock.calls.length,
|
|
161
|
+
attributeRuns: attributeRunSpy.mock.calls.length,
|
|
162
|
+
listenerRuns: listenerRunSpy.mock.calls.length,
|
|
163
|
+
setVar: setVarSpy.mock.calls.length,
|
|
164
|
+
getVar: getVarSpy.mock.calls.length,
|
|
165
|
+
schedulePaint: paintSpy.mock.calls.length,
|
|
166
|
+
querySelectorAll: qsaSpy.mock.calls.length,
|
|
167
|
+
// subtree sizes are sampled at take() time (post-settle), so rendered
|
|
168
|
+
// rows count toward the scope they were rendered into
|
|
169
|
+
queryScopeCost: (() => {
|
|
170
|
+
const selectors = ruleSelectors();
|
|
171
|
+
return qsaSpy.mock.calls.reduce(
|
|
172
|
+
(total, [selector], i) =>
|
|
173
|
+
selectors.has(selector as string)
|
|
174
|
+
? total + countDescendants(qsaSpy.mock.contexts[i] as Element)
|
|
175
|
+
: total,
|
|
176
|
+
0
|
|
177
|
+
);
|
|
178
|
+
})(),
|
|
179
|
+
matches: matchesSpy.mock.calls.length,
|
|
180
|
+
closest: closestSpy.mock.calls.length,
|
|
181
|
+
parentElement: parentElementSpy.mock.calls.length,
|
|
182
|
+
setAttribute: setAttrSpy.mock.calls.length,
|
|
183
|
+
removeAttribute: removeAttrSpy.mock.calls.length,
|
|
184
|
+
textContent: textContentSpy.mock.calls.length,
|
|
185
|
+
importNode: importNodeSpy.mock.calls.length,
|
|
186
|
+
}),
|
|
187
|
+
stop: () => {
|
|
188
|
+
runSpy.mockRestore();
|
|
189
|
+
variableRunSpy.mockRestore();
|
|
190
|
+
attributeRunSpy.mockRestore();
|
|
191
|
+
listenerRunSpy.mockRestore();
|
|
192
|
+
setVarSpy.mockRestore();
|
|
193
|
+
getVarSpy.mockRestore();
|
|
194
|
+
paintSpy.mockRestore();
|
|
195
|
+
qsaSpy.mockRestore();
|
|
196
|
+
matchesSpy.mockRestore();
|
|
197
|
+
closestSpy.mockRestore();
|
|
198
|
+
parentElementSpy.mockRestore();
|
|
199
|
+
setAttrSpy.mockRestore();
|
|
200
|
+
removeAttrSpy.mockRestore();
|
|
201
|
+
textContentSpy.mockRestore();
|
|
202
|
+
importNodeSpy.mockRestore();
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export const expectComplexity = (budget: ComplexityBudget) => {
|
|
208
|
+
expect(budget).toMatchSnapshot("complexity");
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* happy-dom caches `matches()` / `querySelectorAll()` per node and only
|
|
213
|
+
* drops the cache when that node, or a node the match walked through,
|
|
214
|
+
* changes. A `:has()` or sibling-combinator result goes stale after a
|
|
215
|
+
* descendant / sibling changes on its own. Browsers do not cache. Call
|
|
216
|
+
* from `beforeAll` in suites that rely on those selectors; returns the
|
|
217
|
+
* restore function for `afterAll`.
|
|
218
|
+
*/
|
|
219
|
+
export const bypassSelectorCache = () => {
|
|
220
|
+
const cacheOf = (node: Node) => {
|
|
221
|
+
const sym = Object.getOwnPropertySymbols(node).find(
|
|
222
|
+
(s) => s.description === "cache"
|
|
223
|
+
);
|
|
224
|
+
return sym
|
|
225
|
+
? (
|
|
226
|
+
node as unknown as Record<
|
|
227
|
+
symbol,
|
|
228
|
+
Record<string, Map<string, unknown>>
|
|
229
|
+
>
|
|
230
|
+
)[sym]
|
|
231
|
+
: undefined;
|
|
232
|
+
};
|
|
233
|
+
const patch = <T extends Node>(
|
|
234
|
+
proto: T,
|
|
235
|
+
name: "matches" | "querySelectorAll"
|
|
236
|
+
) => {
|
|
237
|
+
const original = (proto as unknown as Record<string, Function>)[name];
|
|
238
|
+
(proto as unknown as Record<string, Function>)[name] = function (
|
|
239
|
+
this: Node,
|
|
240
|
+
selector: string
|
|
241
|
+
) {
|
|
242
|
+
cacheOf(this)?.[name]?.delete(selector);
|
|
243
|
+
return original.call(this, selector);
|
|
244
|
+
};
|
|
245
|
+
return () => {
|
|
246
|
+
(proto as unknown as Record<string, Function>)[name] = original;
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
const restores = [
|
|
250
|
+
patch(Element.prototype, "matches"),
|
|
251
|
+
patch(Element.prototype, "querySelectorAll"),
|
|
252
|
+
patch(Document.prototype, "querySelectorAll"),
|
|
253
|
+
];
|
|
254
|
+
return () => restores.forEach((restore) => restore());
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/** One `document.startViewTransition()` call seen by the stub. */
|
|
258
|
+
export type StubViewTransition = {
|
|
259
|
+
types: Set<string>;
|
|
260
|
+
ready: Promise<void>;
|
|
261
|
+
updateCallbackDone: Promise<void>;
|
|
262
|
+
finished: Promise<void>;
|
|
263
|
+
/** `update` was invoked / its promise settled. */
|
|
264
|
+
isUpdating: boolean;
|
|
265
|
+
isUpdated: boolean;
|
|
266
|
+
isSkipped: boolean;
|
|
267
|
+
skipTransition: () => void;
|
|
268
|
+
/**
|
|
269
|
+
* Invoke `update` (once) and resolve when it settled. Automatic a task
|
|
270
|
+
* after the call unless the stub was installed with `autoUpdate: false`.
|
|
271
|
+
*/
|
|
272
|
+
runUpdate: () => Promise<void>;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Browser-like `document.startViewTransition` for happy-dom (which has
|
|
277
|
+
* none). Like Chromium: `update` runs a task later (after the old-state
|
|
278
|
+
* capture), `updateCallbackDone` / `ready` settle with it, `finished`
|
|
279
|
+
* after `animationMs`; `document.activeViewTransition` is set meanwhile,
|
|
280
|
+
* and starting a transition while one is active skips the active one.
|
|
281
|
+
* Pass `withTypes: false` for an engine that rejects the options object
|
|
282
|
+
* (no transition types), and
|
|
283
|
+
* `autoUpdate: false` to call each transition's `runUpdate()` yourself
|
|
284
|
+
* (to assert the pending phase). `restore()` removes it again.
|
|
285
|
+
*/
|
|
286
|
+
export const installViewTransitionStub = ({
|
|
287
|
+
animationMs = 0,
|
|
288
|
+
withTypes = true,
|
|
289
|
+
autoUpdate = true,
|
|
290
|
+
}: { animationMs?: number; withTypes?: boolean; autoUpdate?: boolean } = {}) => {
|
|
291
|
+
const doc = document as Document & Record<string, any>;
|
|
292
|
+
const calls: StubViewTransition[] = [];
|
|
293
|
+
doc.startViewTransition = (
|
|
294
|
+
arg?: (() => unknown) | { update?: () => unknown; types?: string[] }
|
|
295
|
+
) => {
|
|
296
|
+
if (typeof arg !== "function" && !withTypes) {
|
|
297
|
+
throw new TypeError("parameter 1 is not of type 'Function'");
|
|
298
|
+
}
|
|
299
|
+
const update = typeof arg === "function" ? arg : arg?.update;
|
|
300
|
+
const types = new Set(typeof arg === "function" ? [] : (arg?.types ?? []));
|
|
301
|
+
let settleUpdate!: (error?: unknown) => void;
|
|
302
|
+
let finish!: () => void;
|
|
303
|
+
const updateCallbackDone = new Promise<void>((resolve, reject) => {
|
|
304
|
+
settleUpdate = (error) => (error ? reject(error) : resolve());
|
|
305
|
+
});
|
|
306
|
+
const finished = new Promise<void>((resolve) => (finish = resolve));
|
|
307
|
+
let updating: Promise<void> | undefined;
|
|
308
|
+
const transition: StubViewTransition = {
|
|
309
|
+
types,
|
|
310
|
+
ready: updateCallbackDone,
|
|
311
|
+
updateCallbackDone,
|
|
312
|
+
finished,
|
|
313
|
+
isUpdating: false,
|
|
314
|
+
isUpdated: false,
|
|
315
|
+
isSkipped: false,
|
|
316
|
+
skipTransition: () => {
|
|
317
|
+
transition.isSkipped = true;
|
|
318
|
+
finish();
|
|
319
|
+
},
|
|
320
|
+
runUpdate: () =>
|
|
321
|
+
(updating ??= (async () => {
|
|
322
|
+
transition.isUpdating = true;
|
|
323
|
+
try {
|
|
324
|
+
await update?.();
|
|
325
|
+
transition.isUpdated = true;
|
|
326
|
+
settleUpdate();
|
|
327
|
+
} catch (error) {
|
|
328
|
+
settleUpdate(error);
|
|
329
|
+
}
|
|
330
|
+
setTimeout(finish, animationMs);
|
|
331
|
+
})()),
|
|
332
|
+
};
|
|
333
|
+
const active = doc.activeViewTransition as StubViewTransition | undefined;
|
|
334
|
+
active?.skipTransition();
|
|
335
|
+
doc.activeViewTransition = transition;
|
|
336
|
+
finished.then(() => {
|
|
337
|
+
if (doc.activeViewTransition === transition) {
|
|
338
|
+
doc.activeViewTransition = null;
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
calls.push(transition);
|
|
342
|
+
// the stub never lets a rejected `updateCallbackDone` go unhandled
|
|
343
|
+
updateCallbackDone.catch(() => {});
|
|
344
|
+
if (autoUpdate) setTimeout(transition.runUpdate, 0);
|
|
345
|
+
return transition;
|
|
346
|
+
};
|
|
347
|
+
return {
|
|
348
|
+
calls,
|
|
349
|
+
restore: () => {
|
|
350
|
+
delete doc.startViewTransition;
|
|
351
|
+
delete doc.activeViewTransition;
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
};
|