@ethlete/cli 3.0.0-next.24 → 3.0.0-next.26
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 +126 -0
- package/design/define-call.ts +69 -0
- package/design/define-part.ts +43 -9
- package/design/embed-page.ts +37 -4
- package/design/icon-sheet.ts +151 -0
- package/design/logo-sheet.ts +52 -0
- package/design/mark-part.ts +79 -2
- package/design/motion-drive.ts +215 -0
- package/design/motion-runtime.ts +224 -5
- package/design/motion-tokens.ts +177 -0
- package/design/spring.ts +53 -0
- package/design/token-sheet.ts +87 -0
- package/design/web/frame-fill.ts +4 -0
- package/design/web/frame-scheme.ts +44 -0
- package/design/web/frame.ts +239 -31
- package/package.json +1 -1
- package/src/lib/design/calls-tsconfig.d.ts +2 -0
- package/src/lib/design/calls-tsconfig.js +11 -5
- package/src/lib/design/calls-tsconfig.js.map +1 -1
- package/src/lib/design/check.d.ts +1 -0
- package/src/lib/design/check.js +62 -6
- package/src/lib/design/check.js.map +1 -1
- package/src/lib/design/contact-sheet.d.ts +9 -0
- package/src/lib/design/contact-sheet.js +38 -4
- package/src/lib/design/contact-sheet.js.map +1 -1
- package/src/lib/design/design-command.js +56 -7
- package/src/lib/design/design-command.js.map +1 -1
- package/src/lib/design/draw-ledger.d.ts +36 -0
- package/src/lib/design/draw-ledger.js +52 -0
- package/src/lib/design/draw-ledger.js.map +1 -0
- package/src/lib/design/force-interactions.d.ts +2 -1
- package/src/lib/design/force-interactions.js +7 -4
- package/src/lib/design/force-interactions.js.map +1 -1
- package/src/lib/design/mcp-command.d.ts +81 -0
- package/src/lib/design/mcp-command.js +422 -0
- package/src/lib/design/mcp-command.js.map +1 -0
- package/src/lib/design/motion-command.d.ts +71 -0
- package/src/lib/design/motion-command.js +308 -0
- package/src/lib/design/motion-command.js.map +1 -0
- package/src/lib/design/motion-endpoint.d.ts +61 -0
- package/src/lib/design/motion-endpoint.js +133 -0
- package/src/lib/design/motion-endpoint.js.map +1 -0
- package/src/lib/design/motion-flow.d.ts +5 -0
- package/src/lib/design/motion-flow.js +29 -0
- package/src/lib/design/motion-flow.js.map +1 -0
- package/src/lib/design/motion-shot.d.ts +43 -0
- package/src/lib/design/motion-shot.js +97 -0
- package/src/lib/design/motion-shot.js.map +1 -0
- package/src/lib/design/paths.d.ts +14 -0
- package/src/lib/design/paths.js +50 -1
- package/src/lib/design/paths.js.map +1 -1
- package/src/lib/design/ready-shots.d.ts +7 -0
- package/src/lib/design/ready-shots.js +22 -1
- package/src/lib/design/ready-shots.js.map +1 -1
- package/src/lib/design/render-packages.d.ts +14 -0
- package/src/lib/design/render-packages.js +18 -3
- package/src/lib/design/render-packages.js.map +1 -1
- package/src/lib/design/render-worker.js +1 -0
- package/src/lib/design/render-worker.js.map +1 -1
- package/src/lib/design/render.js +3 -3
- package/src/lib/design/render.js.map +1 -1
- package/src/lib/design/serve.d.ts +2 -2
- package/src/lib/design/serve.js +191 -21
- package/src/lib/design/serve.js.map +1 -1
- package/src/lib/design/shot-command.d.ts +13 -4
- package/src/lib/design/shot-command.js +30 -24
- package/src/lib/design/shot-command.js.map +1 -1
- package/src/lib/design/shot-store.d.ts +24 -0
- package/src/lib/design/shot-store.js +120 -0
- package/src/lib/design/shot-store.js.map +1 -0
- package/src/lib/design/shot.d.ts +37 -1
- package/src/lib/design/shot.js +185 -34
- package/src/lib/design/shot.js.map +1 -1
- package/src/lib/design/sketch-command.js +197 -51
- package/src/lib/design/sketch-command.js.map +1 -1
- package/src/lib/design/ssr-shims.d.ts +2 -0
- package/src/lib/design/ssr-shims.js +12 -1
- package/src/lib/design/ssr-shims.js.map +1 -1
- package/src/lib/design/studio-api.d.ts +31 -0
- package/src/lib/design/studio-api.js +118 -1
- package/src/lib/design/studio-api.js.map +1 -1
- package/src/lib/design/system/check-system.d.ts +6 -2
- package/src/lib/design/system/check-system.js +102 -20
- package/src/lib/design/system/check-system.js.map +1 -1
- package/src/lib/design/system/colour.d.ts +4 -1
- package/src/lib/design/system/colour.js +54 -2
- package/src/lib/design/system/colour.js.map +1 -1
- package/src/lib/design/system/complete-command.d.ts +1 -1
- package/src/lib/design/system/complete-command.js +80 -6
- package/src/lib/design/system/complete-command.js.map +1 -1
- package/src/lib/design/system/complete.d.ts +172 -9
- package/src/lib/design/system/complete.js +792 -167
- package/src/lib/design/system/complete.js.map +1 -1
- package/src/lib/design/system/contrast-check.d.ts +6 -1
- package/src/lib/design/system/contrast-check.js +167 -12
- package/src/lib/design/system/contrast-check.js.map +1 -1
- package/src/lib/design/system/drawn-open-points.d.ts +16 -0
- package/src/lib/design/system/drawn-open-points.js +43 -0
- package/src/lib/design/system/drawn-open-points.js.map +1 -0
- package/src/lib/design/system/exceptions.d.ts +59 -0
- package/src/lib/design/system/exceptions.js +132 -0
- package/src/lib/design/system/exceptions.js.map +1 -0
- package/src/lib/design/system/flows/flow.d.ts +3 -0
- package/src/lib/design/system/flows/flow.js +5 -3
- package/src/lib/design/system/flows/flow.js.map +1 -1
- package/src/lib/design/system/handoff.d.ts +11 -0
- package/src/lib/design/system/handoff.js +30 -0
- package/src/lib/design/system/handoff.js.map +1 -0
- package/src/lib/design/system/inventory-approval.d.ts +41 -0
- package/src/lib/design/system/inventory-approval.js +71 -0
- package/src/lib/design/system/inventory-approval.js.map +1 -0
- package/src/lib/design/system/inventory.d.ts +65 -0
- package/src/lib/design/system/inventory.js +168 -0
- package/src/lib/design/system/inventory.js.map +1 -0
- package/src/lib/design/system/list-system.d.ts +4 -1
- package/src/lib/design/system/list-system.js +11 -3
- package/src/lib/design/system/list-system.js.map +1 -1
- package/src/lib/design/system/open-points.d.ts +101 -0
- package/src/lib/design/system/open-points.js +156 -0
- package/src/lib/design/system/open-points.js.map +1 -0
- package/src/lib/design/system/part-ready.d.ts +52 -0
- package/src/lib/design/system/part-ready.js +101 -0
- package/src/lib/design/system/part-ready.js.map +1 -0
- package/src/lib/design/system/read-open-points.d.ts +27 -0
- package/src/lib/design/system/read-open-points.js +81 -0
- package/src/lib/design/system/read-open-points.js.map +1 -0
- package/src/lib/design/system/ready-command.d.ts +7 -0
- package/src/lib/design/system/ready-command.js +65 -0
- package/src/lib/design/system/ready-command.js.map +1 -0
- package/src/lib/design/system/render-check.d.ts +2 -0
- package/src/lib/design/system/render-check.js +216 -32
- package/src/lib/design/system/render-check.js.map +1 -1
- package/src/lib/design/system/resolve-product.d.ts +4 -4
- package/src/lib/design/system/resolve-product.js +22 -7
- package/src/lib/design/system/resolve-product.js.map +1 -1
- package/src/lib/design/system/rules/implements.d.ts +7 -0
- package/src/lib/design/system/rules/implements.js +43 -0
- package/src/lib/design/system/rules/implements.js.map +1 -0
- package/src/lib/design/system/rules/motion.d.ts +2 -0
- package/src/lib/design/system/rules/motion.js +26 -1
- package/src/lib/design/system/rules/motion.js.map +1 -1
- package/src/lib/design/system/rules/option-values.d.ts +15 -0
- package/src/lib/design/system/rules/option-values.js +145 -0
- package/src/lib/design/system/rules/option-values.js.map +1 -0
- package/src/lib/design/system/rules/stale-replace.js +80 -3
- package/src/lib/design/system/rules/stale-replace.js.map +1 -1
- package/src/lib/design/system/rules/token-selectors.js +1 -1
- package/src/lib/design/system/rules/token-selectors.js.map +1 -1
- package/src/lib/design/system/sketch/baseline.d.ts +11 -2
- package/src/lib/design/system/sketch/baseline.js +4 -2
- package/src/lib/design/system/sketch/baseline.js.map +1 -1
- package/src/lib/design/system/sketch/check-sketches.d.ts +1 -0
- package/src/lib/design/system/sketch/check-sketches.js +4 -2
- package/src/lib/design/system/sketch/check-sketches.js.map +1 -1
- package/src/lib/design/system/sketch/host-edit.d.ts +4 -0
- package/src/lib/design/system/sketch/host-edit.js +68 -0
- package/src/lib/design/system/sketch/host-edit.js.map +1 -0
- package/src/lib/design/system/sketch/merge.d.ts +3 -2
- package/src/lib/design/system/sketch/merge.js +79 -12
- package/src/lib/design/system/sketch/merge.js.map +1 -1
- package/src/lib/design/system/sketch/merged-parts.d.ts +11 -1
- package/src/lib/design/system/sketch/merged-parts.js +24 -3
- package/src/lib/design/system/sketch/merged-parts.js.map +1 -1
- package/src/lib/design/system/sketch/no-visible-change.d.ts +6 -0
- package/src/lib/design/system/sketch/no-visible-change.js +25 -1
- package/src/lib/design/system/sketch/no-visible-change.js.map +1 -1
- package/src/lib/design/system/sketch/overlay.js +11 -3
- package/src/lib/design/system/sketch/overlay.js.map +1 -1
- package/src/lib/design/system/sketch/part-definition.d.ts +36 -0
- package/src/lib/design/system/sketch/part-definition.js +56 -2
- package/src/lib/design/system/sketch/part-definition.js.map +1 -1
- package/src/lib/design/system/sketch/sketch.d.ts +47 -0
- package/src/lib/design/system/sketch/sketch.js +137 -44
- package/src/lib/design/system/sketch/sketch.js.map +1 -1
- package/src/lib/design/system/sketch/unchanged-states.d.ts +4 -1
- package/src/lib/design/system/sketch/unchanged-states.js +9 -5
- package/src/lib/design/system/sketch/unchanged-states.js.map +1 -1
- package/src/lib/design/system/sketch/unused-closure.d.ts +12 -0
- package/src/lib/design/system/sketch/unused-closure.js +195 -0
- package/src/lib/design/system/sketch/unused-closure.js.map +1 -0
- package/src/lib/design/system/sketch/unused-files.d.ts +1 -1
- package/src/lib/design/system/sketch/unused-files.js +6 -3
- package/src/lib/design/system/sketch/unused-files.js.map +1 -1
- package/src/lib/design/system/system-modules.js +6 -9
- package/src/lib/design/system/system-modules.js.map +1 -1
- package/src/lib/design/system/system-tsconfig.d.ts +2 -1
- package/src/lib/design/system/system-tsconfig.js +10 -6
- package/src/lib/design/system/system-tsconfig.js.map +1 -1
- package/src/lib/design/system/ts-source.d.ts +3 -0
- package/src/lib/design/system/ts-source.js +21 -4
- package/src/lib/design/system/ts-source.js.map +1 -1
- package/src/lib/design/thumb.d.ts +31 -4
- package/src/lib/design/thumb.js +105 -40
- package/src/lib/design/thumb.js.map +1 -1
|
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.completenessOf = exports.completenessGapLineOf = exports.completenessGapsOf = void 0;
|
|
12
|
+
exports.partsCompletenessOf = exports.completenessOf = exports.inventoryIdsOf = exports.seePart = exports.PLAIN_CONTROL_SELECTOR = exports.SDK_CONTROLS = exports.isMissingStateGap = exports.completenessGapLineOf = exports.completenessGapsOf = exports.findingKeyOf = exports.isFindingOf = exports.smallChangeHintsOf = exports.controlStateGapsOf = exports.isControlPart = exports.isInteractionState = exports.declaredControlsOf = void 0;
|
|
13
|
+
const fs_1 = require("fs");
|
|
13
14
|
const path_1 = require("path");
|
|
14
15
|
const browser_1 = require("../browser");
|
|
15
16
|
const force_interactions_1 = require("../force-interactions");
|
|
@@ -17,13 +18,41 @@ const serve_1 = require("../serve");
|
|
|
17
18
|
const shot_1 = require("../shot");
|
|
18
19
|
const thumb_1 = require("../thumb");
|
|
19
20
|
const resolve_product_1 = require("./resolve-product");
|
|
21
|
+
const contact_sheet_1 = require("../contact-sheet");
|
|
20
22
|
const baseline_1 = require("./sketch/baseline");
|
|
21
23
|
const merged_parts_1 = require("./sketch/merged-parts");
|
|
22
24
|
const system_modules_1 = require("./system-modules");
|
|
23
|
-
const
|
|
25
|
+
const exceptions_1 = require("./exceptions");
|
|
26
|
+
const inventory_approval_1 = require("./inventory-approval");
|
|
27
|
+
const option_values_1 = require("./rules/option-values");
|
|
28
|
+
const KINDS = {
|
|
29
|
+
disclosure: { surface: 'body', closed: 'collapsed' },
|
|
30
|
+
button: { adds: ['active'] },
|
|
31
|
+
select: { surface: 'open', options: { interactions: ['hover', 'focus-visible'], selected: true } },
|
|
32
|
+
menu: { surface: 'open', options: { interactions: ['hover', 'focus-visible', 'active'], selected: false } },
|
|
33
|
+
row: { selects: true, virtualFocus: true },
|
|
34
|
+
field: { noActive: true },
|
|
35
|
+
tooltip: { surface: 'tooltip' },
|
|
36
|
+
};
|
|
37
|
+
const CHECKED_KINDS = Object.keys(KINDS);
|
|
24
38
|
const INPUT_INTERACTIONS = { pointer: ['hover', 'active'], keyboard: ['focus-visible'] };
|
|
39
|
+
const MARKS = {
|
|
40
|
+
selected: 'aria-selected="true", aria-checked="true", aria-pressed="true" or aria-current',
|
|
41
|
+
disabled: 'disabled or aria-disabled="true"',
|
|
42
|
+
};
|
|
43
|
+
const CONTROL_MARKS = Object.assign(Object.assign({}, MARKS), { disabled: `${MARKS.disabled} on it or a data-et-control-part in it` });
|
|
25
44
|
const isRecord = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
26
|
-
const requiredInteractionsOf = (control) =>
|
|
45
|
+
const requiredInteractionsOf = (control) => {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
return [
|
|
48
|
+
...new Set([
|
|
49
|
+
...control.input
|
|
50
|
+
.flatMap((input) => { var _a; return (_a = INPUT_INTERACTIONS[input]) !== null && _a !== void 0 ? _a : []; })
|
|
51
|
+
.filter((interaction) => { var _a; return !(((_a = KINDS[control.kind]) === null || _a === void 0 ? void 0 : _a.noActive) && interaction === 'active'); }),
|
|
52
|
+
...((_b = (_a = KINDS[control.kind]) === null || _a === void 0 ? void 0 : _a.adds) !== null && _b !== void 0 ? _b : []),
|
|
53
|
+
]),
|
|
54
|
+
];
|
|
55
|
+
};
|
|
27
56
|
const gridGapOf = (options) => {
|
|
28
57
|
const { grid, states, interactions } = options;
|
|
29
58
|
const fix = `draw its states with interactionRows, which gives the grid a column per interaction (${interactions.join(', ')})`;
|
|
@@ -55,21 +84,102 @@ const declaredControlsOf = (value) => isRecord(value)
|
|
|
55
84
|
surfaces: isRecord(declared['surfaces'])
|
|
56
85
|
? Object.fromEntries(Object.entries(declared['surfaces']).filter((entry) => typeof entry[1] === 'string'))
|
|
57
86
|
: {},
|
|
87
|
+
disabled: declared['disabled'] === true,
|
|
58
88
|
},
|
|
59
89
|
];
|
|
60
90
|
}))
|
|
61
91
|
: null;
|
|
92
|
+
exports.declaredControlsOf = declaredControlsOf;
|
|
93
|
+
const INTERACTION_WORDS = new Set(['hover', 'hovered', 'focus', 'focused', 'pressed', 'active', 'selected']);
|
|
94
|
+
const NAME_WORDS = new Set([...INTERACTION_WORDS, 'visible', 'within', 'invalid', 'disabled', 'checked', 'open']);
|
|
95
|
+
const wordsOf = (state) => state
|
|
96
|
+
.replace(/([a-z0-9])([A-Z])/gu, '$1-$2')
|
|
97
|
+
.toLowerCase()
|
|
98
|
+
.split(/[^a-z0-9]+/u)
|
|
99
|
+
.filter((word) => word !== '');
|
|
100
|
+
/**
|
|
101
|
+
* Whether a state name shows an interaction: its last word, split at `-`, `_`, spaces and camelCase, or the whole name
|
|
102
|
+
* is hover, hovered, focus, focused, pressed, active or selected. `row-hover`, `collapseFocus` and `focus-visible`
|
|
103
|
+
* are; `active-only` and `inactive` are not.
|
|
104
|
+
*/
|
|
105
|
+
const isInteractionState = (state) => {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
const words = wordsOf(state);
|
|
108
|
+
while (words.length > 1 && ['visible', 'within'].includes((_a = words[words.length - 1]) !== null && _a !== void 0 ? _a : ''))
|
|
109
|
+
words.pop();
|
|
110
|
+
return INTERACTION_WORDS.has((_b = words[words.length - 1]) !== null && _b !== void 0 ? _b : '');
|
|
111
|
+
};
|
|
112
|
+
exports.isInteractionState = isInteractionState;
|
|
113
|
+
/** Whether a part may draw interaction states: it is no page, and its `controls` declare a control of a checked kind. */
|
|
114
|
+
const isControlPart = (options) => {
|
|
115
|
+
var _a;
|
|
116
|
+
return !options.part.startsWith('pages/') &&
|
|
117
|
+
Object.values((_a = (0, exports.declaredControlsOf)(options.controls)) !== null && _a !== void 0 ? _a : {}).some((control) => CHECKED_KINDS.includes(control.kind));
|
|
118
|
+
};
|
|
119
|
+
exports.isControlPart = isControlPart;
|
|
120
|
+
/**
|
|
121
|
+
* One `CONTROL-STATE` gap per element whose interaction states a part draws that is no control part. The element is
|
|
122
|
+
* the state name without its interaction words (`row-hover` names the row), or `control` when nothing is left.
|
|
123
|
+
*/
|
|
124
|
+
const controlStateGapsOf = (options) => {
|
|
125
|
+
var _a, _b;
|
|
126
|
+
const { part, states } = options;
|
|
127
|
+
if ((0, exports.isControlPart)(options))
|
|
128
|
+
return [];
|
|
129
|
+
const elements = new Map();
|
|
130
|
+
for (const state of states.filter(exports.isInteractionState)) {
|
|
131
|
+
const element = wordsOf(state)
|
|
132
|
+
.filter((word) => !NAME_WORDS.has(word))
|
|
133
|
+
.join('-') || 'control';
|
|
134
|
+
elements.set(element, [...((_a = elements.get(element)) !== null && _a !== void 0 ? _a : []), state]);
|
|
135
|
+
}
|
|
136
|
+
const why = part.startsWith('pages/')
|
|
137
|
+
? 'a page is no control part'
|
|
138
|
+
: `${part} declares no control of a kind ${CHECKED_KINDS.join(', ')}`;
|
|
139
|
+
const declaresNone = Object.keys((_b = (0, exports.declaredControlsOf)(options.controls)) !== null && _b !== void 0 ? _b : {}).length === 0;
|
|
140
|
+
const fixOf = (element) => part.startsWith('components/') && declaresNone
|
|
141
|
+
? `declare its controls (Q164), or extract the ${element} as its own control part`
|
|
142
|
+
: `extract the ${element} as its own control part`;
|
|
143
|
+
return [...elements].map(([element, shown]) => ({
|
|
144
|
+
code: 'CONTROL-STATE',
|
|
145
|
+
target: part,
|
|
146
|
+
detail: `${shown.length === 1 ? 'state' : 'states'} ${statesShownOf(shown)} draw${shown.length === 1 ? 's' : ''} an interaction (hover, focus, pressed, active or selected), which only a control part draws, and ${why} - ${fixOf(element)} and draw ${shown.length === 1 ? 'this state' : 'these states'} there`,
|
|
147
|
+
}));
|
|
148
|
+
};
|
|
149
|
+
exports.controlStateGapsOf = controlStateGapsOf;
|
|
150
|
+
const SMALL_CHANGE = 0.1;
|
|
151
|
+
/**
|
|
152
|
+
* One hint per state of an element or component that is no control part whose pixels differ from its first state in
|
|
153
|
+
* under a tenth of the frame. Pages compose parts, so they get none; interaction states are left to `CONTROL-STATE`.
|
|
154
|
+
*/
|
|
155
|
+
const smallChangeHintsOf = (options) => options.part.startsWith('pages/') || (0, exports.isControlPart)(options)
|
|
156
|
+
? []
|
|
157
|
+
: options.changes
|
|
158
|
+
.filter(({ state, share }) => share > 0 && share < SMALL_CHANGE && !(0, exports.isInteractionState)(state))
|
|
159
|
+
.map(({ state, base, share }) => `HINT ${options.part} ${state} - changes ${(share * 100).toFixed(1)} % of the frame against ${base} - extract the changed element as its own part`);
|
|
160
|
+
exports.smallChangeHintsOf = smallChangeHintsOf;
|
|
161
|
+
/** Whether a discovered element is an undeclared or unclassified finding of the part, not owned or declared. */
|
|
162
|
+
const isFindingOf = (declared) => (found) => {
|
|
163
|
+
var _a;
|
|
164
|
+
return ((_a = found.owner) !== null && _a !== void 0 ? _a : null) === null &&
|
|
165
|
+
!found.within.some((id) => { var _a, _b; return ((_b = (_a = declared[id]) === null || _a === void 0 ? void 0 : _a.part) !== null && _b !== void 0 ? _b : null) !== null; }) &&
|
|
166
|
+
!(found.kind === 'control' && found.marked !== null);
|
|
167
|
+
};
|
|
168
|
+
exports.isFindingOf = isFindingOf;
|
|
169
|
+
const findingKeyOf = (found) => `${found.kind} ${found.what} ${found.path}`;
|
|
170
|
+
exports.findingKeyOf = findingKeyOf;
|
|
171
|
+
const NATIVE_CONTROLS = ['button', 'a[href]', 'input:not([type="hidden"])', 'select', 'textarea', 'summary'];
|
|
62
172
|
const discoveryGapsOf = (options) => {
|
|
63
173
|
var _a, _b, _c, _d;
|
|
64
174
|
const { part, declared, frames } = options;
|
|
65
|
-
const
|
|
175
|
+
const isFinding = (0, exports.isFindingOf)(declared);
|
|
66
176
|
const groups = new Map();
|
|
67
177
|
for (const frame of frames.filter((seen) => seen.interaction === null)) {
|
|
68
178
|
const counts = new Map();
|
|
69
179
|
for (const found of (_a = frame.found) !== null && _a !== void 0 ? _a : []) {
|
|
70
|
-
if (
|
|
180
|
+
if (!isFinding(found))
|
|
71
181
|
continue;
|
|
72
|
-
const key =
|
|
182
|
+
const key = (0, exports.findingKeyOf)(found);
|
|
73
183
|
const group = (_b = groups.get(key)) !== null && _b !== void 0 ? _b : { found, states: [], count: 0 };
|
|
74
184
|
counts.set(key, ((_c = counts.get(key)) !== null && _c !== void 0 ? _c : 0) + 1);
|
|
75
185
|
if (!group.states.includes(frame.state))
|
|
@@ -81,13 +191,14 @@ const discoveryGapsOf = (options) => {
|
|
|
81
191
|
return [...groups.values()]
|
|
82
192
|
.sort((a, b) => a.found.path.localeCompare(b.found.path) || a.found.what.localeCompare(b.found.what))
|
|
83
193
|
.map(({ found, states, count }) => {
|
|
84
|
-
const
|
|
85
|
-
const where = `${found.what} at ${found.path} in ${shown}${count > 1 ? ` (${count} of them)` : ''}`;
|
|
194
|
+
const where = `${found.what} at ${found.path} in ${statesShownOf(states)}${count > 1 ? ` (${count} of them)` : ''}`;
|
|
86
195
|
return found.kind === 'control'
|
|
87
196
|
? {
|
|
88
197
|
code: 'CONTROL-UNDECLARED',
|
|
89
198
|
target: part,
|
|
90
|
-
detail: `${where} -
|
|
199
|
+
detail: `${where} - ${NATIVE_CONTROLS.includes(found.what)
|
|
200
|
+
? 'mark it with control(markup, name) from @ethlete/components/design/control and declare the name in controls'
|
|
201
|
+
: 'mark it with data-et-control and declare it in controls'}`,
|
|
91
202
|
}
|
|
92
203
|
: {
|
|
93
204
|
code: 'CONTROL-UNCLASSIFIED',
|
|
@@ -96,18 +207,96 @@ const discoveryGapsOf = (options) => {
|
|
|
96
207
|
};
|
|
97
208
|
});
|
|
98
209
|
};
|
|
99
|
-
const
|
|
210
|
+
const isUndeclaredControlGap = (gap) => gap.code === 'CONTROL-UNDECLARED';
|
|
211
|
+
const statesShownOf = (states) => states.length > 4 ? `${states.slice(0, 3).join(', ')} and ${states.length - 3} more` : states.join(', ');
|
|
212
|
+
const overrideGapsOf = (options) => {
|
|
213
|
+
var _a, _b;
|
|
214
|
+
const groups = new Map();
|
|
215
|
+
for (const frame of options.frames.filter((seen) => seen.interaction === null))
|
|
216
|
+
for (const override of (_a = frame.overrides) !== null && _a !== void 0 ? _a : []) {
|
|
217
|
+
const key = `${override.owner} ${override.selector} ${override.path}`;
|
|
218
|
+
const group = (_b = groups.get(key)) !== null && _b !== void 0 ? _b : { override, states: [] };
|
|
219
|
+
if (!group.states.includes(frame.state))
|
|
220
|
+
group.states.push(frame.state);
|
|
221
|
+
groups.set(key, group);
|
|
222
|
+
}
|
|
223
|
+
return [...groups.values()]
|
|
224
|
+
.sort((a, b) => a.override.owner.localeCompare(b.override.owner) || a.override.path.localeCompare(b.override.path))
|
|
225
|
+
.map(({ override, states }) => ({
|
|
226
|
+
code: 'CONTROL-OVERRIDDEN',
|
|
227
|
+
target: options.part,
|
|
228
|
+
detail: `"${override.selector}" sets ${override.properties.join(', ')} on ${override.path}, drawn by ${override.owner}, in ${statesShownOf(states)} - change ${override.owner} instead, or mark the control with data-et-control and declare it in controls`,
|
|
229
|
+
}));
|
|
230
|
+
};
|
|
231
|
+
const ownerGapsOf = (options) => {
|
|
232
|
+
var _a, _b, _c;
|
|
233
|
+
const { part, declared, frames, referenced } = options;
|
|
234
|
+
const named = new Set(Object.values(declared).map((control) => control.part));
|
|
235
|
+
const owned = new Map();
|
|
236
|
+
for (const frame of frames.filter((seen) => seen.interaction === null))
|
|
237
|
+
for (const found of (_a = frame.found) !== null && _a !== void 0 ? _a : []) {
|
|
238
|
+
const owner = (_b = found.owner) !== null && _b !== void 0 ? _b : null;
|
|
239
|
+
if (owner === null || named.has(owner))
|
|
240
|
+
continue;
|
|
241
|
+
owned.set(owner, ((_c = owned.get(owner)) !== null && _c !== void 0 ? _c : new Set()).add(`${found.what} ${found.path}`));
|
|
242
|
+
}
|
|
243
|
+
return [...owned.entries()]
|
|
244
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
245
|
+
.flatMap(([owner, controls]) => {
|
|
246
|
+
const used = referenced[owner];
|
|
247
|
+
const drawn = `${owner} draws ${controls.size} control(s) here`;
|
|
248
|
+
if (typeof used === 'string')
|
|
249
|
+
return [{ code: 'CONTROL-PART-INCOMPLETE', target: part, detail: `${drawn}: ${used}` }];
|
|
250
|
+
if (!used || used.length === 0)
|
|
251
|
+
return [];
|
|
252
|
+
return [
|
|
253
|
+
{
|
|
254
|
+
code: 'CONTROL-PART-INCOMPLETE',
|
|
255
|
+
target: part,
|
|
256
|
+
detail: `${drawn} and is not design complete: ${used.length} gap(s) - run design complete ${owner}`,
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
const ownersOf = (frames) => [
|
|
262
|
+
...new Set(frames
|
|
263
|
+
.filter((seen) => seen.interaction === null)
|
|
264
|
+
.flatMap((seen) => { var _a; return ((_a = seen.found) !== null && _a !== void 0 ? _a : []).map((found) => { var _a; return (_a = found.owner) !== null && _a !== void 0 ? _a : null; }); })
|
|
265
|
+
.filter((owner) => owner !== null)),
|
|
266
|
+
];
|
|
267
|
+
const undefinedClassesGapOf = (options) => {
|
|
100
268
|
var _a;
|
|
269
|
+
const { forced, what, interaction } = options;
|
|
270
|
+
const seen = forced.find((force) => { var _a; return (_a = force.undefinedClasses) === null || _a === void 0 ? void 0 : _a.length; });
|
|
271
|
+
if (!seen)
|
|
272
|
+
return null;
|
|
273
|
+
const classes = (_a = seen.undefinedClasses) !== null && _a !== void 0 ? _a : [];
|
|
274
|
+
return `state ${seen.state} forces ${interaction} on ${what}, but no stylesheet defines its ${classes.length === 1 ? 'class' : 'classes'} ${classes.join(', ')} - Tailwind generates no utility of that name; use a colour or value the product's theme defines`;
|
|
275
|
+
};
|
|
276
|
+
const interactionGapOf = (options) => {
|
|
277
|
+
var _a, _b;
|
|
101
278
|
const { frames, id, interaction } = options;
|
|
102
279
|
const forced = frames.flatMap((frame) => frame.controls
|
|
103
280
|
.filter((control) => control.id === id)
|
|
104
281
|
.flatMap((control) => control.forced.filter((force) => force.interaction === interaction))
|
|
105
|
-
.map((force) => ({ state: frame.state, differs: force.differs })));
|
|
282
|
+
.map((force) => ({ state: frame.state, differs: force.differs, undefinedClasses: force.undefinedClasses })));
|
|
106
283
|
if (forced.some((force) => force.differs))
|
|
107
284
|
return null;
|
|
108
285
|
if (forced.length > 0)
|
|
109
|
-
return `state ${(
|
|
110
|
-
return `no state draws it ${interaction} - draw a state that forces ${interaction} on the data-et-control element, e.g. with forceInteraction`;
|
|
286
|
+
return ((_a = undefinedClassesGapOf({ forced, what: 'it', interaction })) !== null && _a !== void 0 ? _a : `state ${(_b = forced[0]) === null || _b === void 0 ? void 0 : _b.state} forces ${interaction} on it, but its box draws no different pixel than at rest - if a descendant draws ${interaction}, mark it with data-et-control-part`);
|
|
287
|
+
return `no state draws it ${interaction} - draw a state that forces ${interaction} on the data-et-control element, e.g. with forceInteraction - a data-et-control-part in it takes the force too`;
|
|
288
|
+
};
|
|
289
|
+
const keyboardActiveRowGapOf = (options) => {
|
|
290
|
+
const { instances, forcedGap } = options;
|
|
291
|
+
const active = instances.filter((seen) => seen.keyboard);
|
|
292
|
+
const plain = instances.filter((seen) => !seen.keyboard && !seen.selected && !seen.disabled);
|
|
293
|
+
if (active.length === 0)
|
|
294
|
+
return `${forcedGap}; no state marks it data-active-source="keyboard" either`;
|
|
295
|
+
if (plain.length === 0)
|
|
296
|
+
return `a state marks it data-active-source="keyboard", but no state draws it plain (not selected, active or disabled) to compare it with`;
|
|
297
|
+
if (active.some((seen) => plain.some((other) => other.look !== seen.look)))
|
|
298
|
+
return null;
|
|
299
|
+
return `it looks the same with data-active-source="keyboard" as plain - give the active look its own colours`;
|
|
111
300
|
};
|
|
112
301
|
const stillGapOf = (options) => {
|
|
113
302
|
var _a;
|
|
@@ -118,30 +307,139 @@ const stillGapOf = (options) => {
|
|
|
118
307
|
return `state ${frame.state} changes ${(_a = force.still) === null || _a === void 0 ? void 0 : _a.join(', ')} in one frame - give them a transition with the product's motion tokens`;
|
|
119
308
|
return null;
|
|
120
309
|
};
|
|
121
|
-
const
|
|
122
|
-
const {
|
|
310
|
+
const lookGapOf = (options) => {
|
|
311
|
+
const { seen, flag, what, marks = MARKS } = options;
|
|
312
|
+
const on = seen.filter((one) => one.flag);
|
|
313
|
+
const off = seen.filter((one) => !one.flag);
|
|
314
|
+
if (on.length === 0)
|
|
315
|
+
return `no state draws ${what} ${flag} - mark it with ${marks[flag]} in a state`;
|
|
316
|
+
if (off.length === 0)
|
|
317
|
+
return `every state draws ${what} ${flag} - draw it without ${flag} too, to compare`;
|
|
318
|
+
if (!on.some((one) => off.some((other) => one.look !== other.look)))
|
|
319
|
+
return `${what} looks the same ${flag} as without - give the ${flag} look its own colours`;
|
|
320
|
+
return null;
|
|
321
|
+
};
|
|
322
|
+
const forcedGapOf = (options) => {
|
|
323
|
+
var _a, _b;
|
|
324
|
+
const { forced, what, interaction } = options;
|
|
325
|
+
if (forced.some((force) => force.differs))
|
|
326
|
+
return null;
|
|
327
|
+
if (forced.length > 0)
|
|
328
|
+
return ((_a = undefinedClassesGapOf(options)) !== null && _a !== void 0 ? _a : `state ${(_b = forced[0]) === null || _b === void 0 ? void 0 : _b.state} forces ${interaction} on ${what}, but its box draws no different pixel than at rest`);
|
|
329
|
+
return `no state draws ${what} ${interaction} - force ${interaction} on it, e.g. with forceInteraction`;
|
|
330
|
+
};
|
|
331
|
+
const surfaceGapsOf = (options) => {
|
|
332
|
+
const { frames, id, control, states, surface, closed } = options;
|
|
123
333
|
const rest = frames.filter((frame) => frame.interaction === null);
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
const
|
|
334
|
+
const state = control.surfaces[surface];
|
|
335
|
+
const surfaceId = `${id}.${surface}`;
|
|
336
|
+
const shows = (frame) => frame.surfaces.some((seen) => seen.id === surfaceId && seen.shown);
|
|
127
337
|
const drawsControl = (frame) => frame.controls.some((seen) => seen.id === id);
|
|
128
338
|
const gaps = [];
|
|
129
|
-
|
|
130
|
-
|
|
339
|
+
if (closed && !rest.some((frame) => drawsControl(frame) && !shows(frame)))
|
|
340
|
+
gaps.push([closed, `every state that draws it also shows its ${surface} - draw a state with ${surfaceId} closed`]);
|
|
341
|
+
if (state === undefined)
|
|
342
|
+
gaps.push([surface, `declares no surfaces.${surface} - name the state that draws it open`]);
|
|
343
|
+
else if (!states.includes(state))
|
|
344
|
+
gaps.push([surface, `surfaces.${surface} names state ${state}, which the part does not declare`]);
|
|
345
|
+
else if (!rest.some((frame) => frame.state === state && drawsControl(frame) && shows(frame)))
|
|
346
|
+
gaps.push([
|
|
347
|
+
surface,
|
|
348
|
+
`state ${state} draws no visible, non-empty data-et-surface="${surfaceId}" next to data-et-control="${id}"`,
|
|
349
|
+
]);
|
|
350
|
+
return gaps;
|
|
351
|
+
};
|
|
352
|
+
const KEYBOARD_ACTIVE = 'aria-activedescendant or data-active-source="keyboard"';
|
|
353
|
+
const isKeyboardActive = (option) => !!option.named || !!option.keyboard;
|
|
354
|
+
const isPlain = (option) => !option.selected && !isKeyboardActive(option) && option.forced.length === 0;
|
|
355
|
+
const keyboardActiveGapOf = (options) => {
|
|
356
|
+
var _a, _b;
|
|
357
|
+
const { frames, shownIn, forcedGap } = options;
|
|
358
|
+
const active = frames
|
|
359
|
+
.map((frame) => ({ state: frame.state, options: shownIn(frame) }))
|
|
360
|
+
.filter((frame) => frame.options.some(isKeyboardActive));
|
|
361
|
+
const compared = active.filter((frame) => frame.options.some(isPlain));
|
|
362
|
+
if (active.length === 0)
|
|
363
|
+
return `${forcedGap}; no option is marked active by ${KEYBOARD_ACTIVE} either`;
|
|
364
|
+
if (compared.length === 0)
|
|
365
|
+
return `state ${(_a = active[0]) === null || _a === void 0 ? void 0 : _a.state} marks an option active by ${KEYBOARD_ACTIVE}, but draws no plain option (not selected, active or forced) to compare it with`;
|
|
366
|
+
if (compared.some((frame) => frame.options.some((option) => isKeyboardActive(option) && frame.options.some((plain) => isPlain(plain) && plain.look !== option.look))))
|
|
367
|
+
return null;
|
|
368
|
+
return `state ${(_b = compared[0]) === null || _b === void 0 ? void 0 : _b.state} marks an option active by ${KEYBOARD_ACTIVE}, but it looks the same as a plain option - give the active option its own look`;
|
|
369
|
+
};
|
|
370
|
+
const optionGapsOf = (options) => {
|
|
371
|
+
const { frames, surfaceId, interactions } = options;
|
|
372
|
+
const shownIn = (frame) => frame.surfaces.filter((seen) => seen.id === surfaceId && seen.shown).flatMap((seen) => { var _a; return (_a = seen.options) !== null && _a !== void 0 ? _a : []; });
|
|
373
|
+
const what = `an option in ${surfaceId}`;
|
|
374
|
+
const gaps = [];
|
|
375
|
+
for (const interaction of interactions) {
|
|
376
|
+
const forced = frames.flatMap((frame) => shownIn(frame).flatMap((option) => option.forced
|
|
377
|
+
.filter((force) => force.interaction === interaction)
|
|
378
|
+
.map((force) => ({ state: frame.state, differs: force.differs, undefinedClasses: force.undefinedClasses }))));
|
|
379
|
+
const gap = forcedGapOf({ forced, what, interaction });
|
|
380
|
+
const missing = gap && interaction === 'focus-visible' ? keyboardActiveGapOf({ frames, shownIn, forcedGap: gap }) : gap;
|
|
381
|
+
if (missing)
|
|
382
|
+
gaps.push([`option-${interaction}`, missing]);
|
|
383
|
+
}
|
|
384
|
+
if (!options.selected)
|
|
385
|
+
return gaps;
|
|
386
|
+
const seen = frames
|
|
387
|
+
.filter((frame) => frame.interaction === null)
|
|
388
|
+
.flatMap((frame) => shownIn(frame)
|
|
389
|
+
.filter((option) => option.forced.length === 0 && !isKeyboardActive(option))
|
|
390
|
+
.map((option) => ({ look: option.look, flag: option.selected })));
|
|
391
|
+
const selected = lookGapOf({ seen, flag: 'selected', what });
|
|
392
|
+
if (selected)
|
|
393
|
+
gaps.push(['option-selected', selected]);
|
|
394
|
+
return gaps;
|
|
395
|
+
};
|
|
396
|
+
const requirementGapsOf = (options) => {
|
|
397
|
+
var _a;
|
|
398
|
+
const { frames, id, control } = options;
|
|
399
|
+
const kind = (_a = KINDS[control.kind]) !== null && _a !== void 0 ? _a : {};
|
|
400
|
+
const own = control.part === null;
|
|
401
|
+
const instances = frames
|
|
402
|
+
.filter((frame) => frame.interaction === null)
|
|
403
|
+
.flatMap((frame) => frame.controls.filter((seen) => seen.id === id && seen.forced.length === 0));
|
|
404
|
+
const gaps = [];
|
|
405
|
+
for (const interaction of own ? requiredInteractionsOf(control) : []) {
|
|
406
|
+
const forcedGap = interactionGapOf({ frames, id, interaction });
|
|
407
|
+
const gap = forcedGap && interaction === 'focus-visible' && kind.virtualFocus
|
|
408
|
+
? keyboardActiveRowGapOf({ instances, forcedGap })
|
|
409
|
+
: forcedGap;
|
|
131
410
|
if (gap)
|
|
132
411
|
gaps.push([interaction, gap]);
|
|
133
412
|
}
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
'
|
|
143
|
-
|
|
144
|
-
|
|
413
|
+
if (own && kind.selects) {
|
|
414
|
+
const selected = lookGapOf({
|
|
415
|
+
seen: instances.map((seen) => { var _a; return ({ look: seen.look, flag: (_a = seen.selected) !== null && _a !== void 0 ? _a : false }); }),
|
|
416
|
+
flag: 'selected',
|
|
417
|
+
what: 'it',
|
|
418
|
+
});
|
|
419
|
+
const forced = frames.flatMap((frame) => frame.controls
|
|
420
|
+
.filter((seen) => seen.id === id && seen.selected)
|
|
421
|
+
.flatMap((seen) => seen.forced.filter((force) => force.interaction === 'hover'))
|
|
422
|
+
.map((force) => ({ state: frame.state, differs: force.differs, undefinedClasses: force.undefinedClasses })));
|
|
423
|
+
const hovered = forcedGapOf({ forced, what: 'it selected', interaction: 'hover' });
|
|
424
|
+
if (selected)
|
|
425
|
+
gaps.push(['selected', selected]);
|
|
426
|
+
if (hovered)
|
|
427
|
+
gaps.push(['selected-hover', hovered]);
|
|
428
|
+
}
|
|
429
|
+
if (own && control.disabled) {
|
|
430
|
+
const disabled = lookGapOf({
|
|
431
|
+
seen: instances.map((seen) => { var _a; return ({ look: seen.look, flag: (_a = seen.disabled) !== null && _a !== void 0 ? _a : false }); }),
|
|
432
|
+
flag: 'disabled',
|
|
433
|
+
what: 'it',
|
|
434
|
+
marks: CONTROL_MARKS,
|
|
435
|
+
});
|
|
436
|
+
if (disabled)
|
|
437
|
+
gaps.push(['disabled', disabled]);
|
|
438
|
+
}
|
|
439
|
+
if (kind.surface)
|
|
440
|
+
gaps.push(...surfaceGapsOf(Object.assign(Object.assign({}, options), { surface: kind.surface, closed: kind.closed })));
|
|
441
|
+
if (own && kind.options && kind.surface)
|
|
442
|
+
gaps.push(...optionGapsOf(Object.assign({ frames, surfaceId: `${id}.${kind.surface}` }, kind.options)));
|
|
145
443
|
return gaps;
|
|
146
444
|
};
|
|
147
445
|
/**
|
|
@@ -150,8 +448,8 @@ const disclosureGapsOf = (options) => {
|
|
|
150
448
|
*/
|
|
151
449
|
const completenessGapsOf = (options) => {
|
|
152
450
|
var _a;
|
|
153
|
-
const { part, states, frames, reviewed, grid, referenced = {} } = options;
|
|
154
|
-
const declared = (_a = declaredControlsOf(options.controls)) !== null && _a !== void 0 ? _a : {};
|
|
451
|
+
const { part, states, frames, reviewed, grid, referenced = {}, inventory } = options;
|
|
452
|
+
const declared = (_a = (0, exports.declaredControlsOf)(options.controls)) !== null && _a !== void 0 ? _a : {};
|
|
155
453
|
const gaps = [];
|
|
156
454
|
if (!reviewed)
|
|
157
455
|
gaps.push({
|
|
@@ -208,7 +506,7 @@ const completenessGapsOf = (options) => {
|
|
|
208
506
|
gaps.push({ code: 'CONTROL-UNDRAWN', target: `${part}.${id}`, detail: `no state draws data-et-control="${id}"` });
|
|
209
507
|
continue;
|
|
210
508
|
}
|
|
211
|
-
for (const [requirement, detail] of
|
|
509
|
+
for (const [requirement, detail] of requirementGapsOf({ frames, id, control, states }))
|
|
212
510
|
gaps.push({ code: 'REQUIREMENT-MISSING', target: `${part}.${id}.${requirement}`, detail });
|
|
213
511
|
for (const interaction of interactions) {
|
|
214
512
|
const gap = stillGapOf({ frames, id, interaction });
|
|
@@ -216,11 +514,27 @@ const completenessGapsOf = (options) => {
|
|
|
216
514
|
gaps.push({ code: 'CONTROL-TRANSITION-MISSING', target: `${part}.${id}.${interaction}`, detail: gap });
|
|
217
515
|
}
|
|
218
516
|
}
|
|
219
|
-
return [
|
|
517
|
+
return [
|
|
518
|
+
...gaps,
|
|
519
|
+
...discoveryGapsOf({ part, declared, frames }).filter((gap) => !isUndeclaredControlGap(gap)),
|
|
520
|
+
...ownerGapsOf({ part, declared, frames, referenced }),
|
|
521
|
+
...overrideGapsOf({ part, frames }),
|
|
522
|
+
...inventoryGapsOf({ part, inventory }),
|
|
523
|
+
];
|
|
220
524
|
};
|
|
221
525
|
exports.completenessGapsOf = completenessGapsOf;
|
|
526
|
+
const inventoryGapsOf = ({ part, inventory }) => {
|
|
527
|
+
const reason = inventory ? (0, inventory_approval_1.inventoryReasonOf)(inventory) : null;
|
|
528
|
+
return reason === null
|
|
529
|
+
? []
|
|
530
|
+
: [{ code: 'INVENTORY-UNAPPROVED', target: part, detail: `${reason} - check and approve it in Studio` }];
|
|
531
|
+
};
|
|
222
532
|
const completenessGapLineOf = (gap) => `${gap.code} ${gap.target} - ${gap.detail}`;
|
|
223
533
|
exports.completenessGapLineOf = completenessGapLineOf;
|
|
534
|
+
const MISSING_STATE_CODES = ['CONTROL-UNDRAWN', 'CONTROL-GRID-MISSING', 'REQUIREMENT-MISSING'];
|
|
535
|
+
/** Whether a gap is a declared control that no state draws, or that lacks a drawn interaction, look or surface. */
|
|
536
|
+
const isMissingStateGap = (gap) => MISSING_STATE_CODES.includes(gap.code);
|
|
537
|
+
exports.isMissingStateGap = isMissingStateGap;
|
|
224
538
|
const NO_MOTION = '*, *::before, *::after { transition: none !important; animation: none !important; }';
|
|
225
539
|
const PAD = 8;
|
|
226
540
|
const COLOUR_PROPERTIES = [
|
|
@@ -264,6 +578,28 @@ const coversOf = (property) => {
|
|
|
264
578
|
return ['all', 'border', 'border-color', property.replace(/-color$/, ''), property];
|
|
265
579
|
return ['all', property];
|
|
266
580
|
};
|
|
581
|
+
const undefinedVariantClasses = (at) => `((at) => {
|
|
582
|
+
const control = document.querySelector('[data-et-complete="' + at + '"]');
|
|
583
|
+
const selectors = [];
|
|
584
|
+
const collect = (rules) => {
|
|
585
|
+
for (const rule of rules) {
|
|
586
|
+
if (rule.selectorText) selectors.push(rule.selectorText);
|
|
587
|
+
if (rule.cssRules) collect(rule.cssRules);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
for (const sheet of document.styleSheets) {
|
|
591
|
+
try {
|
|
592
|
+
collect(sheet.cssRules);
|
|
593
|
+
} catch {}
|
|
594
|
+
}
|
|
595
|
+
const tokens = new Set([control, ...control.querySelectorAll('*')].flatMap((element) => [...element.classList]));
|
|
596
|
+
return [...tokens].filter((token) => token.includes(':') && !selectors.some((selector) => selector.includes('.' + CSS.escape(token))));
|
|
597
|
+
})(${JSON.stringify(at)})`;
|
|
598
|
+
const VARIANT_WORDS = { hover: 'hover', active: 'active', 'focus-visible': 'focus' };
|
|
599
|
+
const isVariantOf = (token, interaction) => token
|
|
600
|
+
.split(':')
|
|
601
|
+
.slice(0, -1)
|
|
602
|
+
.some((variant) => { var _a; return variant.includes((_a = VARIANT_WORDS[interaction]) !== null && _a !== void 0 ? _a : interaction); });
|
|
267
603
|
const stillChangesOf = (rest, forced) => forced.names.flatMap((name, element) => {
|
|
268
604
|
var _a;
|
|
269
605
|
const { properties, durations } = (_a = forced.transitions[element]) !== null && _a !== void 0 ? _a : { properties: [], durations: [] };
|
|
@@ -301,7 +637,7 @@ const INTERACTIVE_ROLES = [
|
|
|
301
637
|
'textbox',
|
|
302
638
|
'treeitem',
|
|
303
639
|
];
|
|
304
|
-
|
|
640
|
+
exports.SDK_CONTROLS = [
|
|
305
641
|
'[et-button]',
|
|
306
642
|
'[et-icon-button]',
|
|
307
643
|
'[et-text-button]',
|
|
@@ -341,48 +677,23 @@ const SDK_CONTROLS = [
|
|
|
341
677
|
'et-time-input',
|
|
342
678
|
'et-toggletip',
|
|
343
679
|
];
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
];
|
|
680
|
+
const PLAIN_CONTROLS = [...NATIVE_CONTROLS, '[tabindex]', ...INTERACTIVE_ROLES.map((role) => `[role="${role}"]`)];
|
|
681
|
+
const LIKELY_CONTROLS = [...exports.SDK_CONTROLS, ...PLAIN_CONTROLS];
|
|
682
|
+
const LOOK_PROPERTIES = [...COLOUR_PROPERTIES, 'opacity', 'box-shadow', 'font-weight', 'text-decoration-line'];
|
|
683
|
+
const OPTION_SELECTOR = '[role="option"], [role^="menuitem"], [data-et-option]';
|
|
684
|
+
const COMPOSITE_ROLES = ['grid', 'listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'];
|
|
685
|
+
const FOCUS_CONTAINER = COMPOSITE_ROLES.flatMap((role) => [
|
|
686
|
+
`[role="${role}"][tabindex="-1"]`,
|
|
687
|
+
`[role="${role}"][aria-activedescendant]`,
|
|
688
|
+
]).join(', ');
|
|
689
|
+
exports.PLAIN_CONTROL_SELECTOR = `:is(${PLAIN_CONTROLS.join(', ')}):not(${FOCUS_CONTAINER})`;
|
|
355
690
|
const discoverControls = `((root) => {
|
|
356
691
|
const LIKELY = ${JSON.stringify(LIKELY_CONTROLS)};
|
|
357
692
|
const likely = LIKELY.join(', ');
|
|
358
|
-
const
|
|
693
|
+
const container = ${JSON.stringify(FOCUS_CONTAINER)};
|
|
694
|
+
const isLikely = (element) => element.matches(likely) && !element.matches(container);
|
|
695
|
+
const sdk = ${JSON.stringify(exports.SDK_CONTROLS.join(', '))};
|
|
359
696
|
const rendered = (element) => element.getClientRects().length > 0;
|
|
360
|
-
const nameOf = (element) => {
|
|
361
|
-
const cls = [...element.classList].find((name) => /^[a-z][\\w-]*$/i.test(name));
|
|
362
|
-
return element.tagName.toLowerCase() + (cls ? '.' + cls : '');
|
|
363
|
-
};
|
|
364
|
-
const pathOf = (element) => {
|
|
365
|
-
const names = [];
|
|
366
|
-
for (let at = element; at && at !== root; at = at.parentElement) names.unshift(nameOf(at));
|
|
367
|
-
return (names.length > 4 ? '... > ' : '') + names.slice(-4).join(' > ');
|
|
368
|
-
};
|
|
369
|
-
const splitTop = (selector) => {
|
|
370
|
-
const parts = [];
|
|
371
|
-
let depth = 0;
|
|
372
|
-
let from = 0;
|
|
373
|
-
for (let at = 0; at < selector.length; at++) {
|
|
374
|
-
const char = selector[at];
|
|
375
|
-
if (char === '\\\\') at++;
|
|
376
|
-
else if (char === '(' || char === '[') depth++;
|
|
377
|
-
else if (char === ')' || char === ']') depth--;
|
|
378
|
-
else if (char === ',' && depth === 0) {
|
|
379
|
-
parts.push(selector.slice(from, at).trim());
|
|
380
|
-
from = at + 1;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
parts.push(selector.slice(from).trim());
|
|
384
|
-
return parts.filter(Boolean);
|
|
385
|
-
};
|
|
386
697
|
const HOVER = /(?<!\\\\):hover(?![\\w-])/g;
|
|
387
698
|
const FORCED_HOVER = /:is\\(:hover,\\s*\\[data-et-force~="?hover"?\\]\\)/g;
|
|
388
699
|
const hoveredOf = (written) => {
|
|
@@ -408,7 +719,7 @@ const discoverControls = `((root) => {
|
|
|
408
719
|
for (const rule of rules) {
|
|
409
720
|
if (rule instanceof CSSStyleRule) {
|
|
410
721
|
const own = splitTop(rule.selectorText).map((selector) =>
|
|
411
|
-
|
|
722
|
+
nested(selector, parent),
|
|
412
723
|
);
|
|
413
724
|
for (const selector of own) {
|
|
414
725
|
const target = hoveredOf(selector);
|
|
@@ -426,43 +737,166 @@ const discoverControls = `((root) => {
|
|
|
426
737
|
walk(sheet.cssRules, null);
|
|
427
738
|
} catch {}
|
|
428
739
|
}
|
|
740
|
+
const markerOf = (at, element) =>
|
|
741
|
+
at.getAttribute('data-et-control') ?? (element.matches(${JSON.stringify(OPTION_SELECTOR)}) ? at.getAttribute('data-et-surface')?.split('.')[0] : null) ?? null;
|
|
429
742
|
const withinOf = (element) => {
|
|
430
743
|
const ids = [];
|
|
431
|
-
for (let at = element; at && at !== root; at = at.parentElement)
|
|
432
|
-
|
|
744
|
+
for (let at = element; at && at !== root; at = at.parentElement) {
|
|
745
|
+
const id = markerOf(at, element);
|
|
746
|
+
if (id !== null) ids.push(id);
|
|
747
|
+
}
|
|
433
748
|
return ids;
|
|
434
749
|
};
|
|
435
750
|
const markedOf = (element) => {
|
|
436
751
|
if (element.hasAttribute('data-et-control')) return element.getAttribute('data-et-control');
|
|
437
752
|
for (let at = element.parentElement; at && at !== root; at = at.parentElement) {
|
|
438
|
-
if (at
|
|
439
|
-
|
|
753
|
+
if (isLikely(at)) return null;
|
|
754
|
+
const id = markerOf(at, element);
|
|
755
|
+
if (id !== null) return id;
|
|
440
756
|
}
|
|
441
757
|
return null;
|
|
442
758
|
};
|
|
443
759
|
const found = [];
|
|
444
760
|
for (const element of root.querySelectorAll('*')) {
|
|
445
761
|
if (!rendered(element)) continue;
|
|
446
|
-
const what = LIKELY.find((selector) => element.matches(selector));
|
|
762
|
+
const what = !element.matches(container) && LIKELY.find((selector) => element.matches(selector));
|
|
447
763
|
if (what) {
|
|
448
764
|
const inSdk = element.parentElement.closest(sdk);
|
|
449
765
|
if (inSdk !== null && root.contains(inSdk)) continue;
|
|
450
|
-
found.push({ kind: 'control', what: what.startsWith('[') ? element.tagName.toLowerCase() + what : what, path: pathOf(element), marked: markedOf(element), within: withinOf(element) });
|
|
766
|
+
found.push({ kind: 'control', what: what.startsWith('[') ? element.tagName.toLowerCase() + what : what, path: pathOf(element), marked: markedOf(element), within: withinOf(element), owner: ownerOf(element)?.key ?? null, box: frameBoxOf(element) });
|
|
451
767
|
continue;
|
|
452
768
|
}
|
|
453
769
|
const cursor = getComputedStyle(element).cursor;
|
|
454
770
|
const pointer = cursor === 'pointer' && getComputedStyle(element.parentElement).cursor !== 'pointer';
|
|
455
771
|
if (!pointer && !hovered.has(element)) continue;
|
|
456
|
-
|
|
772
|
+
let inControl = false;
|
|
773
|
+
for (let at = element; at && !inControl; at = at.parentElement) inControl = at.hasAttribute('data-et-control') || isLikely(at);
|
|
774
|
+
if (inControl) continue;
|
|
457
775
|
const reason = [pointer ? 'cursor: pointer' : '', hovered.has(element) ? ':hover rule' : ''].filter(Boolean).join(' and ');
|
|
458
|
-
found.push({ kind: 'unclassified', what: nameOf(element) + ' with ' + reason, path: pathOf(element), marked: null, within: [] });
|
|
776
|
+
found.push({ kind: 'unclassified', what: nameOf(element) + ' with ' + reason, path: pathOf(element), marked: null, within: [], owner: ownerOf(element)?.key ?? null, box: frameBoxOf(element) });
|
|
459
777
|
}
|
|
460
778
|
return found;
|
|
461
779
|
})`;
|
|
462
|
-
const
|
|
780
|
+
const ROOT_LAYOUT = [
|
|
781
|
+
'^margin',
|
|
782
|
+
'^inset',
|
|
783
|
+
'^(top|right|bottom|left)$',
|
|
784
|
+
'^(min-|max-)?(width|height|inline-size|block-size)$',
|
|
785
|
+
'^position$',
|
|
786
|
+
'^z-index$',
|
|
787
|
+
'^flex(-grow|-shrink|-basis)?$',
|
|
788
|
+
'^grid-(area|row|column)',
|
|
789
|
+
'^(align|justify|place)-self$',
|
|
790
|
+
'^order$',
|
|
791
|
+
'^(transform|translate|rotate|scale)$',
|
|
792
|
+
];
|
|
793
|
+
const findOverrides = `((ownCss) => {
|
|
794
|
+
const LAYOUT = new RegExp(${JSON.stringify(ROOT_LAYOUT.join('|'))});
|
|
795
|
+
const USER_ACTION = /(?<!\\\\):(hover|focus-visible|focus-within|focus|active)(?![\\w-])/g;
|
|
796
|
+
const PSEUDO_ELEMENT = /::?(before|after|marker|placeholder|selection|backdrop|file-selector-button|first-line|first-letter)(?![\\w-])/g;
|
|
797
|
+
const declaredByHost = (element) => {
|
|
798
|
+
for (let at = element; at && at !== root.parentElement; at = at.parentElement)
|
|
799
|
+
if (at.hasAttribute('data-et-control') && ownerOf(at) === null) return true;
|
|
800
|
+
return false;
|
|
801
|
+
};
|
|
802
|
+
const overrides = [];
|
|
803
|
+
const seen = new Set();
|
|
804
|
+
const check = (selector, style) => {
|
|
805
|
+
const properties = [...new Set(style.cssText.split(';').map((declaration) => declaration.split(':')[0].trim()).filter(Boolean))];
|
|
806
|
+
if (properties.length === 0) return;
|
|
807
|
+
let matched = [];
|
|
808
|
+
try {
|
|
809
|
+
matched = [...root.querySelectorAll(selector.replace(PSEUDO_ELEMENT, '').replace(USER_ACTION, ':where(*)'))];
|
|
810
|
+
} catch {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
for (const element of matched) {
|
|
814
|
+
const owner = ownerOf(element);
|
|
815
|
+
if (owner === null || declaredByHost(element)) continue;
|
|
816
|
+
const look = owner.at === element ? properties.filter((property) => !LAYOUT.test(property)) : properties;
|
|
817
|
+
const key = selector + ' ' + pathOf(element);
|
|
818
|
+
if (look.length === 0 || seen.has(key)) continue;
|
|
819
|
+
seen.add(key);
|
|
820
|
+
overrides.push({ owner: owner.key, selector, path: pathOf(element), properties: look });
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
const walk = (rules, parent) => {
|
|
824
|
+
for (const rule of rules) {
|
|
825
|
+
if (rule instanceof CSSStyleRule) {
|
|
826
|
+
const own = splitTop(rule.selectorText).map((selector) => nested(selector, parent));
|
|
827
|
+
for (const selector of own) check(selector, rule.style);
|
|
828
|
+
if (rule.cssRules && rule.cssRules.length > 0) walk(rule.cssRules, own.join(', '));
|
|
829
|
+
} else if (parent !== null && rule.constructor.name === 'CSSNestedDeclarations') check(parent, rule.style);
|
|
830
|
+
else if (rule.cssRules) walk(rule.cssRules, parent);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
for (const text of ownCss) {
|
|
834
|
+
const sheet = new CSSStyleSheet();
|
|
835
|
+
try {
|
|
836
|
+
sheet.replaceSync(text);
|
|
837
|
+
} catch {
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
walk(sheet.cssRules, null);
|
|
841
|
+
}
|
|
842
|
+
return overrides;
|
|
843
|
+
})`;
|
|
844
|
+
/** A control root and every `data-et-control-part` whose nearest `data-et-control` is that root, in the page. */
|
|
845
|
+
const controlPartsOf = `((control) => [
|
|
846
|
+
control,
|
|
847
|
+
...[...control.querySelectorAll('[data-et-control-part]')].filter((part) => part.parentElement.closest('[data-et-control]') === control),
|
|
848
|
+
])`;
|
|
849
|
+
const seeFrame = (options) => `(() => {
|
|
463
850
|
const root = document.getElementById('root');
|
|
464
851
|
const refused = root.innerText.trim();
|
|
465
852
|
if (refused.startsWith('design-explore:')) return { refused: refused.split('\\n')[0] };
|
|
853
|
+
const ORIGIN = ${options.clip} ? root.getBoundingClientRect() : { left: -scrollX, top: -scrollY };
|
|
854
|
+
const frameBoxOf = (element) => {
|
|
855
|
+
const box = element.getBoundingClientRect();
|
|
856
|
+
return box.width > 0 && box.height > 0
|
|
857
|
+
? { x: Math.round(box.left - ORIGIN.left), y: Math.round(box.top - ORIGIN.top), width: Math.round(box.width), height: Math.round(box.height) }
|
|
858
|
+
: null;
|
|
859
|
+
};
|
|
860
|
+
const OWN = ${JSON.stringify(options.part)};
|
|
861
|
+
const tokensOf = (element, name) => (element.getAttribute(name) ?? '').split(' ').filter(Boolean);
|
|
862
|
+
const ownerOf = (element) => {
|
|
863
|
+
const slotted = tokensOf(element, 'data-et-slot');
|
|
864
|
+
for (let at = element; at; at = at.parentElement)
|
|
865
|
+
for (const key of tokensOf(at, 'data-et-part')) {
|
|
866
|
+
const index = slotted.indexOf(key);
|
|
867
|
+
if (index >= 0) slotted.splice(index, 1);
|
|
868
|
+
else return key === OWN ? null : { key, at };
|
|
869
|
+
}
|
|
870
|
+
return null;
|
|
871
|
+
};
|
|
872
|
+
const nameOf = (element) => {
|
|
873
|
+
const cls = [...element.classList].find((name) => /^[a-z][\\w-]*$/i.test(name));
|
|
874
|
+
return element.tagName.toLowerCase() + (cls ? '.' + cls : '');
|
|
875
|
+
};
|
|
876
|
+
const pathOf = (element) => {
|
|
877
|
+
const names = [];
|
|
878
|
+
for (let at = element; at && at !== root; at = at.parentElement) names.unshift(nameOf(at));
|
|
879
|
+
return (names.length > 4 ? '... > ' : '') + names.slice(-4).join(' > ');
|
|
880
|
+
};
|
|
881
|
+
const splitTop = (selector) => {
|
|
882
|
+
const parts = [];
|
|
883
|
+
let depth = 0;
|
|
884
|
+
let from = 0;
|
|
885
|
+
for (let at = 0; at < selector.length; at++) {
|
|
886
|
+
const char = selector[at];
|
|
887
|
+
if (char === '\\\\') at++;
|
|
888
|
+
else if (char === '(' || char === '[') depth++;
|
|
889
|
+
else if (char === ')' || char === ']') depth--;
|
|
890
|
+
else if (char === ',' && depth === 0) {
|
|
891
|
+
parts.push(selector.slice(from, at).trim());
|
|
892
|
+
from = at + 1;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
parts.push(selector.slice(from).trim());
|
|
896
|
+
return parts.filter(Boolean);
|
|
897
|
+
};
|
|
898
|
+
const nested = (selector, parent) =>
|
|
899
|
+
parent === null ? selector : selector.includes('&') ? selector.replaceAll('&', ':is(' + parent + ')') : ':is(' + parent + ') ' + selector;
|
|
466
900
|
const shown = (element) => {
|
|
467
901
|
const box = element.getBoundingClientRect();
|
|
468
902
|
let { left, top, right, bottom } = box;
|
|
@@ -479,38 +913,79 @@ const seeFrame = (discover) => `(() => {
|
|
|
479
913
|
}
|
|
480
914
|
return element.innerText.trim() !== '' || !!element.querySelector('img, svg, canvas, video, iframe, input, select, textarea');
|
|
481
915
|
};
|
|
482
|
-
const
|
|
916
|
+
const LOOK = ${JSON.stringify(LOOK_PROPERTIES)};
|
|
917
|
+
const lookOf = (element) => {
|
|
918
|
+
const own = LOOK.map((name) => getComputedStyle(element).getPropertyValue(name));
|
|
919
|
+
const inside = [...element.querySelectorAll('*')].map((child) => {
|
|
920
|
+
const style = getComputedStyle(child);
|
|
921
|
+
return LOOK.map((name) => style.getPropertyValue(name)).join(' ');
|
|
922
|
+
});
|
|
923
|
+
return JSON.stringify([own, [...new Set(inside)].sort()]);
|
|
924
|
+
};
|
|
925
|
+
const named = new Set([...root.querySelectorAll('[aria-activedescendant]')].filter((element) => ownerOf(element) === null).map((element) => element.getAttribute('aria-activedescendant')));
|
|
926
|
+
const isSelected = (element) =>
|
|
927
|
+
['aria-selected', 'aria-checked', 'aria-pressed'].some((name) => element.getAttribute(name) === 'true') ||
|
|
928
|
+
(element.hasAttribute('aria-current') && element.getAttribute('aria-current') !== 'false');
|
|
929
|
+
const isDisabled = (element) =>
|
|
930
|
+
element.matches(':disabled') || element.getAttribute('aria-disabled') === 'true' || tokensOf(element, 'data-et-force').includes('disabled');
|
|
931
|
+
const isKeyboard = (element) => element.getAttribute('data-active-source') === 'keyboard';
|
|
932
|
+
let next = 0;
|
|
933
|
+
const tagged = (element) => {
|
|
934
|
+
const at = element.hasAttribute('data-et-complete') ? Number(element.getAttribute('data-et-complete')) : next++;
|
|
483
935
|
element.setAttribute('data-et-complete', String(at));
|
|
484
936
|
const box = element.getBoundingClientRect();
|
|
485
937
|
const x = Math.max(0, box.x + scrollX - ${PAD});
|
|
486
938
|
const y = Math.max(0, box.y + scrollY - ${PAD});
|
|
487
939
|
return {
|
|
488
|
-
id: element.getAttribute('data-et-control'),
|
|
489
940
|
at,
|
|
490
|
-
forced: (element
|
|
941
|
+
forced: tokensOf(element, 'data-et-force'),
|
|
491
942
|
box: box.width > 0 && box.height > 0 ? { x, y, width: box.width + ${PAD * 2}, height: box.height + ${PAD * 2} } : null,
|
|
943
|
+
selected: isSelected(element),
|
|
944
|
+
look: lookOf(element),
|
|
945
|
+
};
|
|
946
|
+
};
|
|
947
|
+
const controls = [...root.querySelectorAll('[data-et-control]')].filter((element) => ownerOf(element) === null).map((element) => {
|
|
948
|
+
const parts = ${controlPartsOf}(element);
|
|
949
|
+
const forced = new Set(parts.flatMap((part) => tokensOf(part, 'data-et-force')));
|
|
950
|
+
for (const part of parts) if (forced.size > 0) part.setAttribute('data-et-force', [...forced].join(' '));
|
|
951
|
+
return {
|
|
952
|
+
id: element.getAttribute('data-et-control'),
|
|
953
|
+
disabled: parts.some(isDisabled),
|
|
954
|
+
keyboard: parts.some(isKeyboard),
|
|
955
|
+
frameBox: frameBoxOf(element),
|
|
956
|
+
...tagged(element),
|
|
492
957
|
};
|
|
493
958
|
});
|
|
494
|
-
const surfaces = [...root.querySelectorAll('[data-et-surface]')].map((element) => ({
|
|
959
|
+
const surfaces = [...root.querySelectorAll('[data-et-surface]')].filter((element) => ownerOf(element) === null).map((element) => ({
|
|
495
960
|
id: element.getAttribute('data-et-surface'),
|
|
496
961
|
shown: shown(element),
|
|
962
|
+
options: [...element.querySelectorAll(${JSON.stringify(OPTION_SELECTOR)})].filter((option) => ownerOf(option) === null && shown(option)).map((option) => ({ ...tagged(option), named: option.id !== '' && named.has(option.id), keyboard: isKeyboard(option) })),
|
|
497
963
|
}));
|
|
498
|
-
|
|
964
|
+
const discover = ${options.discover};
|
|
965
|
+
return {
|
|
966
|
+
controls,
|
|
967
|
+
surfaces,
|
|
968
|
+
found: discover ? ${discoverControls}(root) : [],
|
|
969
|
+
overrides: discover ? ${findOverrides}(${JSON.stringify(options.ownCss)}) : [],
|
|
970
|
+
};
|
|
499
971
|
})()`;
|
|
500
972
|
const forceOn = ({ at, interaction, on, }) => `((at, interaction, on) => {
|
|
501
|
-
const
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
973
|
+
const found = document.querySelector('[data-et-complete="' + at + '"]');
|
|
974
|
+
for (const element of found.hasAttribute('data-et-control') ? ${controlPartsOf}(found) : [found]) {
|
|
975
|
+
const tokens = new Set((element.getAttribute('data-et-force') ?? '').split(' ').filter(Boolean));
|
|
976
|
+
if (on) tokens.add(interaction);
|
|
977
|
+
else tokens.delete(interaction);
|
|
978
|
+
if (tokens.size > 0) element.setAttribute('data-et-force', [...tokens].join(' '));
|
|
979
|
+
else element.removeAttribute('data-et-force');
|
|
980
|
+
}
|
|
507
981
|
})(${JSON.stringify(at)}, ${JSON.stringify(interaction)}, ${JSON.stringify(on)})`;
|
|
508
982
|
const FORCEABLE = new Set(['hover', 'focus-visible', 'active']);
|
|
509
983
|
const seeFrames = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
510
|
-
const { browser, base, frames } = options;
|
|
984
|
+
const { browser, base, frames, part, ownCss } = options;
|
|
511
985
|
const context = yield browser.newContext();
|
|
512
986
|
const seen = [];
|
|
513
987
|
const refused = [];
|
|
988
|
+
const shots = [];
|
|
514
989
|
try {
|
|
515
990
|
const page = yield context.newPage();
|
|
516
991
|
for (const frame of frames) {
|
|
@@ -518,117 +993,256 @@ const seeFrames = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
518
993
|
yield (0, thumb_1.openFrame)(page, `${base}/frame.html?${frame.search}`);
|
|
519
994
|
yield page.addStyleTag({ content: NO_MOTION });
|
|
520
995
|
yield page.evaluate(thumb_1.settled);
|
|
521
|
-
const found = (yield page.evaluate(seeFrame(frame.interaction === null)));
|
|
996
|
+
const found = (yield page.evaluate(seeFrame({ discover: frame.interaction === null, part, ownCss, clip: frame.clip })));
|
|
522
997
|
if ('refused' in found) {
|
|
523
998
|
refused.push(`${frame.state}: ${found.refused}`);
|
|
524
999
|
continue;
|
|
525
1000
|
}
|
|
526
|
-
|
|
527
|
-
|
|
1001
|
+
if (frame.interaction === null) {
|
|
1002
|
+
const box = frame.clip ? yield page.locator('#root').boundingBox() : null;
|
|
1003
|
+
shots.push({
|
|
1004
|
+
state: frame.state,
|
|
1005
|
+
png: yield page.screenshot({ type: 'png', fullPage: true, animations: 'disabled', clip: box !== null && box !== void 0 ? box : undefined }),
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
const forcedOf = (element) => __awaiter(void 0, void 0, void 0, function* () {
|
|
528
1009
|
const forced = [];
|
|
529
|
-
for (const interaction of
|
|
530
|
-
if (!
|
|
1010
|
+
for (const interaction of element.forced.filter((name) => FORCEABLE.has(name))) {
|
|
1011
|
+
if (!element.box) {
|
|
531
1012
|
forced.push({ interaction, differs: false });
|
|
532
1013
|
continue;
|
|
533
1014
|
}
|
|
534
|
-
const shot = () => { var _a; return page.screenshot({ type: 'png', fullPage: true, clip: (_a =
|
|
1015
|
+
const shot = () => { var _a; return page.screenshot({ type: 'png', fullPage: true, clip: (_a = element.box) !== null && _a !== void 0 ? _a : undefined, animations: 'disabled' }); };
|
|
535
1016
|
const drawn = yield shot();
|
|
536
|
-
const drawnColours = (yield page.evaluate(colourSnapshot(
|
|
537
|
-
yield page.evaluate(forceOn({ at:
|
|
1017
|
+
const drawnColours = (yield page.evaluate(colourSnapshot(element.at)));
|
|
1018
|
+
yield page.evaluate(forceOn({ at: element.at, interaction, on: false }));
|
|
538
1019
|
yield page.evaluate(thumb_1.settled);
|
|
539
1020
|
const rest = yield shot();
|
|
540
|
-
const restColours = (yield page.evaluate(colourSnapshot(
|
|
541
|
-
yield page.evaluate(forceOn({ at:
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
1021
|
+
const restColours = (yield page.evaluate(colourSnapshot(element.at)));
|
|
1022
|
+
yield page.evaluate(forceOn({ at: element.at, interaction, on: true }));
|
|
1023
|
+
const differs = !drawn.equals(rest);
|
|
1024
|
+
const undefinedClasses = differs
|
|
1025
|
+
? []
|
|
1026
|
+
: (yield page.evaluate(undefinedVariantClasses(element.at))).filter((token) => isVariantOf(token, interaction));
|
|
1027
|
+
forced.push(Object.assign({ interaction,
|
|
1028
|
+
differs, still: stillChangesOf(restColours, drawnColours) }, (undefinedClasses.length > 0 ? { undefinedClasses } : {})));
|
|
547
1029
|
}
|
|
548
|
-
|
|
1030
|
+
return forced;
|
|
1031
|
+
});
|
|
1032
|
+
const controls = [];
|
|
1033
|
+
const surfaces = [];
|
|
1034
|
+
for (const control of found.controls)
|
|
1035
|
+
controls.push(Object.assign(Object.assign({ id: control.id, forced: yield forcedOf(control), selected: control.selected, disabled: control.disabled, look: control.look }, (control.keyboard ? { keyboard: true } : {})), { box: control.frameBox }));
|
|
1036
|
+
for (const surface of found.surfaces) {
|
|
1037
|
+
const options = [];
|
|
1038
|
+
for (const option of surface.options)
|
|
1039
|
+
options.push(Object.assign(Object.assign({ selected: option.selected, look: option.look, forced: yield forcedOf(option) }, (option.named ? { named: true } : {})), (option.keyboard ? { keyboard: true } : {})));
|
|
1040
|
+
surfaces.push({ id: surface.id, shown: surface.shown, options });
|
|
549
1041
|
}
|
|
550
1042
|
seen.push({
|
|
551
1043
|
state: frame.state,
|
|
552
1044
|
interaction: frame.interaction,
|
|
553
1045
|
controls,
|
|
554
|
-
surfaces
|
|
1046
|
+
surfaces,
|
|
555
1047
|
found: found.found,
|
|
1048
|
+
overrides: found.overrides,
|
|
556
1049
|
});
|
|
557
1050
|
}
|
|
558
1051
|
}
|
|
559
1052
|
finally {
|
|
560
1053
|
yield context.close();
|
|
561
1054
|
}
|
|
562
|
-
|
|
1055
|
+
const [first, ...others] = shots;
|
|
1056
|
+
const shares = first ? yield (0, contact_sheet_1.changedSharesOf)({ browser, base: first.png, others: others.map(({ png }) => png) }) : [];
|
|
1057
|
+
return {
|
|
1058
|
+
seen,
|
|
1059
|
+
refused,
|
|
1060
|
+
changes: others.map(({ state }, index) => {
|
|
1061
|
+
var _a, _b;
|
|
1062
|
+
return ({
|
|
1063
|
+
state,
|
|
1064
|
+
base: (_a = first === null || first === void 0 ? void 0 : first.state) !== null && _a !== void 0 ? _a : '',
|
|
1065
|
+
share: (_b = shares[index]) !== null && _b !== void 0 ? _b : 0,
|
|
1066
|
+
});
|
|
1067
|
+
}),
|
|
1068
|
+
};
|
|
1069
|
+
});
|
|
1070
|
+
/**
|
|
1071
|
+
* Draws every state of one part of the product, at rest and, unless `restOnly`, in each interaction it declares,
|
|
1072
|
+
* once at its first preset or width, and answers what each frame showed, or why its module did not load as a string.
|
|
1073
|
+
*/
|
|
1074
|
+
const seePart = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1075
|
+
var _a, _b, _c, _d, _e;
|
|
1076
|
+
const { product, part, system, base, load, draw, restOnly = false, sketch = null } = options;
|
|
1077
|
+
const key = (0, resolve_product_1.partKeyOf)(part);
|
|
1078
|
+
const loaded = yield load((0, system_modules_1.productModuleIdOf)({ file: part.file, product: product.name, sketch }))
|
|
1079
|
+
.then((module) => ({ module: module }))
|
|
1080
|
+
.catch((error) => ({ error: error instanceof Error ? error.message : String(error) }));
|
|
1081
|
+
if ('error' in loaded)
|
|
1082
|
+
return loaded.error;
|
|
1083
|
+
const definition = loaded.module.default;
|
|
1084
|
+
const states = isRecord(definition === null || definition === void 0 ? void 0 : definition.states) ? Object.keys(definition.states) : [];
|
|
1085
|
+
const interactions = Array.isArray(definition === null || definition === void 0 ? void 0 : definition.interactions)
|
|
1086
|
+
? definition.interactions.filter((name) => force_interactions_1.INTERACTIONS.some((known) => known === name))
|
|
1087
|
+
: [];
|
|
1088
|
+
const widths = Array.isArray(definition === null || definition === void 0 ? void 0 : definition.widths)
|
|
1089
|
+
? definition.widths.filter((width) => typeof width === 'number')
|
|
1090
|
+
: [];
|
|
1091
|
+
const presets = Object.keys((_b = (_a = system.drawnOf(product.name, null)) === null || _a === void 0 ? void 0 : _a.presets) !== null && _b !== void 0 ? _b : {});
|
|
1092
|
+
const preset = part.layer === 'pages' ? ((_c = presets[0]) !== null && _c !== void 0 ? _c : null) : null;
|
|
1093
|
+
const width = preset === null ? ((_d = widths[0]) !== null && _d !== void 0 ? _d : null) : null;
|
|
1094
|
+
const frames = [];
|
|
1095
|
+
for (const state of states)
|
|
1096
|
+
for (const interaction of [null, ...(restOnly ? [] : interactions)]) {
|
|
1097
|
+
const frame = (0, shot_1.partFrameOf)({
|
|
1098
|
+
system,
|
|
1099
|
+
product: product.name,
|
|
1100
|
+
part: key,
|
|
1101
|
+
state,
|
|
1102
|
+
preset,
|
|
1103
|
+
width,
|
|
1104
|
+
sketch,
|
|
1105
|
+
interaction,
|
|
1106
|
+
});
|
|
1107
|
+
if (!(frame instanceof Error))
|
|
1108
|
+
frames.push({
|
|
1109
|
+
state,
|
|
1110
|
+
interaction,
|
|
1111
|
+
search: frame.search.toString(),
|
|
1112
|
+
size: (_e = frame.size) !== null && _e !== void 0 ? _e : null,
|
|
1113
|
+
clip: preset === null,
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
const dir = (0, path_1.dirname)(part.file);
|
|
1117
|
+
const ownCss = (0, fs_1.readdirSync)(dir)
|
|
1118
|
+
.filter((file) => file.endsWith('.css'))
|
|
1119
|
+
.sort()
|
|
1120
|
+
.map((file) => (0, fs_1.readFileSync)((0, path_1.join)(dir, file), 'utf8'));
|
|
1121
|
+
const { seen, refused, changes } = yield draw((browser) => seeFrames({ browser, base, frames, part: key, ownCss }));
|
|
1122
|
+
const owner = (0, inventory_approval_1.partOwnerOf)({ product, part });
|
|
1123
|
+
return {
|
|
1124
|
+
definition,
|
|
1125
|
+
states,
|
|
1126
|
+
seen,
|
|
1127
|
+
refused,
|
|
1128
|
+
changes,
|
|
1129
|
+
addresses: states.map((state) => ({ state, preset, width })),
|
|
1130
|
+
owner,
|
|
1131
|
+
hash: (0, baseline_1.partHashOf)({ product: owner, dir }),
|
|
1132
|
+
};
|
|
563
1133
|
});
|
|
1134
|
+
exports.seePart = seePart;
|
|
1135
|
+
/** The control ids a page draws at rest, the set its inventory approval holds. */
|
|
1136
|
+
const inventoryIdsOf = (frames) => [
|
|
1137
|
+
...new Set(frames.filter((frame) => frame.interaction === null).flatMap((frame) => frame.controls.map(({ id }) => id))),
|
|
1138
|
+
].sort();
|
|
1139
|
+
exports.inventoryIdsOf = inventoryIdsOf;
|
|
564
1140
|
/**
|
|
565
1141
|
* Draws every state of one part, at rest and in each interaction it declares, once at its first preset or width,
|
|
566
|
-
* and answers the gaps of its `controls` declaration, or why it could not draw. A part a control names in `part
|
|
567
|
-
* is checked the same way, and its gaps fail
|
|
1142
|
+
* and answers the gaps of its `controls` declaration, or why it could not draw. A part a control names in `part`,
|
|
1143
|
+
* and an embedded part whose `data-et-part` root draws a control, is checked the same way, and its gaps fail it -
|
|
1144
|
+
* unless it is a part of the product's base, whose gaps stay its own.
|
|
1145
|
+
* A page fails until the user approves its control inventory in Studio. An exception the user approved in Studio
|
|
1146
|
+
* drops the gap it names until the part changes; a pending one still fails. With `sketch`, every part is drawn as
|
|
1147
|
+
* that variant draws it, and a part the variant changes is not merged yet, so it never fails as unreviewed.
|
|
1148
|
+
* A drawn control that no `controls` entry declares is a hint; the sketch check fails on one a sketch adds.
|
|
568
1149
|
*/
|
|
569
1150
|
const completenessOf = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
570
1151
|
var _a, _b, _c, _d;
|
|
571
|
-
const { target, config, product, part } = options;
|
|
1152
|
+
const { target, config, product, part, sketch = null } = options;
|
|
1153
|
+
const system = (0, system_modules_1.createSystemModules)({ target, config });
|
|
1154
|
+
const parts = (_b = (_a = (sketch ? system.drawnOf(product.name, sketch) : product)) === null || _a === void 0 ? void 0 : _a.parts) !== null && _b !== void 0 ? _b : [];
|
|
1155
|
+
if (!parts.some((candidate) => (0, resolve_product_1.partKeyOf)(candidate) === part))
|
|
1156
|
+
return {
|
|
1157
|
+
problem: sketch
|
|
1158
|
+
? `variant "${sketch.variant}" of the sketch on ${sketch.target} in product "${product.name}" has no part "${part}"`
|
|
1159
|
+
: `product "${product.name}" has no part "${part}"`,
|
|
1160
|
+
};
|
|
1161
|
+
const checked = yield (0, exports.partsCompletenessOf)(Object.assign(Object.assign({}, options), { parts: [part] }));
|
|
1162
|
+
if ('unavailable' in checked)
|
|
1163
|
+
return checked;
|
|
1164
|
+
const gaps = (_c = checked.gaps.get(part)) !== null && _c !== void 0 ? _c : [];
|
|
1165
|
+
if (typeof gaps === 'string')
|
|
1166
|
+
return { problem: gaps };
|
|
1167
|
+
const hints = (_d = checked.hints.get(part)) !== null && _d !== void 0 ? _d : [];
|
|
1168
|
+
return Object.assign(Object.assign({ gaps }, (checked.exceptions.length > 0 ? { exceptions: checked.exceptions } : {})), (hints.length > 0 ? { hints } : {}));
|
|
1169
|
+
});
|
|
1170
|
+
exports.completenessOf = completenessOf;
|
|
1171
|
+
/**
|
|
1172
|
+
* Checks each named part as `completenessOf` does, sharing one server, browser and cache, and answers the gaps of
|
|
1173
|
+
* each part, or why it could not be checked as a string.
|
|
1174
|
+
*/
|
|
1175
|
+
const partsCompletenessOf = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1176
|
+
var _a, _b, _c, _d;
|
|
1177
|
+
const { target, config, product, sketch = null } = options;
|
|
572
1178
|
const resolveChromium = (_a = options.chromiumOf) !== null && _a !== void 0 ? _a : browser_1.chromiumOf;
|
|
573
|
-
|
|
574
|
-
|
|
1179
|
+
const system = (0, system_modules_1.createSystemModules)({ target, config });
|
|
1180
|
+
const parts = (_c = (_b = (sketch ? system.drawnOf(product.name, sketch) : product)) === null || _b === void 0 ? void 0 : _b.parts) !== null && _c !== void 0 ? _c : [];
|
|
1181
|
+
const optionValuesOf = (0, option_values_1.optionValuesChecker)({ target, config, parts });
|
|
575
1182
|
if (!(yield resolveChromium(target)))
|
|
576
1183
|
return { unavailable: 'neither the checkout nor this package resolves playwright or playwright-core' };
|
|
577
1184
|
const queue = (0, thumb_1.createDrawQueue)({ target, chromiumOf: resolveChromium });
|
|
578
1185
|
const server = yield (0, serve_1.createDesignServer)({ target, config, listen: true, port: 0 });
|
|
579
1186
|
try {
|
|
580
1187
|
yield server.listen();
|
|
581
|
-
const { port } = (
|
|
582
|
-
const
|
|
583
|
-
const
|
|
1188
|
+
const { port } = (_d = server.httpServer) === null || _d === void 0 ? void 0 : _d.address();
|
|
1189
|
+
const checked = new Map();
|
|
1190
|
+
const exceptions = [];
|
|
1191
|
+
const hints = new Map();
|
|
584
1192
|
const gapsOfPart = (key, chain) => __awaiter(void 0, void 0, void 0, function* () {
|
|
585
|
-
|
|
586
|
-
|
|
1193
|
+
const known = checked.get(key);
|
|
1194
|
+
if (known !== undefined && !chain.includes(key))
|
|
1195
|
+
return known;
|
|
1196
|
+
const gaps = yield freshGapsOfPart(key, chain);
|
|
1197
|
+
checked.set(key, gaps);
|
|
1198
|
+
return gaps;
|
|
1199
|
+
});
|
|
1200
|
+
const freshGapsOfPart = (key, chain) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1201
|
+
var _a, _b, _c, _d;
|
|
1202
|
+
const resolved = parts.find((candidate) => (0, resolve_product_1.partKeyOf)(candidate) === key);
|
|
587
1203
|
if (!resolved)
|
|
588
1204
|
return `product "${product.name}" has no part "${key}"`;
|
|
589
1205
|
if (chain.includes(key))
|
|
590
1206
|
return `its controls name each other in a cycle: ${[...chain, key].join(' > ')}`;
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
: [];
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
width,
|
|
617
|
-
sketch: null,
|
|
618
|
-
interaction,
|
|
619
|
-
});
|
|
620
|
-
if (!(frame instanceof Error))
|
|
621
|
-
frames.push({ state, interaction, search: frame.search.toString(), size: (_d = frame.size) !== null && _d !== void 0 ? _d : null });
|
|
622
|
-
}
|
|
623
|
-
const { seen, refused } = yield queue.run((browser) => seeFrames({ browser, base: `http://localhost:${port}`, frames }));
|
|
624
|
-
const reviewed = (0, merged_parts_1.readMerged)(product.dir)[key] === (0, baseline_1.partHashOf)({ product, dir: (0, path_1.dirname)(resolved.file) });
|
|
1207
|
+
const sight = yield (0, exports.seePart)({
|
|
1208
|
+
product,
|
|
1209
|
+
part: resolved,
|
|
1210
|
+
system,
|
|
1211
|
+
base: `http://localhost:${port}`,
|
|
1212
|
+
load: (id) => server.ssrLoadModule(id),
|
|
1213
|
+
draw: (job) => queue.run(job),
|
|
1214
|
+
sketch,
|
|
1215
|
+
});
|
|
1216
|
+
if (typeof sight === 'string')
|
|
1217
|
+
return [{ code: 'RENDER', target: key, detail: (_a = sight.split('\n')[0]) !== null && _a !== void 0 ? _a : '' }];
|
|
1218
|
+
const { definition, states, seen, refused, changes, owner, hash } = sight;
|
|
1219
|
+
const optionProblems = optionValuesOf(resolved, definition !== null && definition !== void 0 ? definition : {});
|
|
1220
|
+
hints.set(key, [
|
|
1221
|
+
...(0, exports.smallChangeHintsOf)({ part: key, controls: definition === null || definition === void 0 ? void 0 : definition.controls, changes }),
|
|
1222
|
+
...(0, exports.controlStateGapsOf)({ part: key, controls: definition === null || definition === void 0 ? void 0 : definition.controls, states }).map(exports.completenessGapLineOf),
|
|
1223
|
+
...discoveryGapsOf({ part: key, declared: (_b = (0, exports.declaredControlsOf)(definition === null || definition === void 0 ? void 0 : definition.controls)) !== null && _b !== void 0 ? _b : {}, frames: seen })
|
|
1224
|
+
.filter(isUndeclaredControlGap)
|
|
1225
|
+
.map(exports.completenessGapLineOf),
|
|
1226
|
+
...optionProblems
|
|
1227
|
+
.filter((gap) => gap.code === 'OPTION-VALUE-MISSING')
|
|
1228
|
+
.map((gap) => (0, exports.completenessGapLineOf)(Object.assign(Object.assign({}, gap), { target: key }))),
|
|
1229
|
+
]);
|
|
1230
|
+
const sketched = ((_c = product.parts.find((candidate) => (0, resolve_product_1.partKeyOf)(candidate) === key)) === null || _c === void 0 ? void 0 : _c.file) !== resolved.file;
|
|
1231
|
+
const reviewed = sketched || (0, merged_parts_1.readMerged)(owner.dir)[key] === hash;
|
|
625
1232
|
const referenced = {};
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
1233
|
+
const used = [
|
|
1234
|
+
...Object.values((_d = (0, exports.declaredControlsOf)(definition === null || definition === void 0 ? void 0 : definition.controls)) !== null && _d !== void 0 ? _d : {}).map((control) => control.part),
|
|
1235
|
+
...ownersOf(seen),
|
|
1236
|
+
];
|
|
1237
|
+
const isKitPart = (other) => { var _a; return resolved.origin !== 'base' && ((_a = parts.find((candidate) => (0, resolve_product_1.partKeyOf)(candidate) === other)) === null || _a === void 0 ? void 0 : _a.origin) === 'base'; };
|
|
1238
|
+
for (const other of used)
|
|
1239
|
+
if (other !== null && !(other in referenced))
|
|
1240
|
+
referenced[other] = isKitPart(other) ? [] : yield gapsOfPart(other, [...chain, key]);
|
|
1241
|
+
const excepted = (0, exceptions_1.withExceptions)({
|
|
1242
|
+
part: key,
|
|
1243
|
+
exceptions: (0, exceptions_1.readExceptions)(owner.dir),
|
|
1244
|
+
hash,
|
|
1245
|
+
gaps: (0, exports.completenessGapsOf)({
|
|
632
1246
|
part: key,
|
|
633
1247
|
controls: definition === null || definition === void 0 ? void 0 : definition.controls,
|
|
634
1248
|
states,
|
|
@@ -636,11 +1250,22 @@ const completenessOf = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
636
1250
|
reviewed,
|
|
637
1251
|
grid: definition === null || definition === void 0 ? void 0 : definition.grid,
|
|
638
1252
|
referenced,
|
|
1253
|
+
inventory: resolved.layer === 'pages'
|
|
1254
|
+
? (0, inventory_approval_1.inventoryStateOf)({ approval: (0, inventory_approval_1.readInventory)(owner.dir)[key], ids: (0, exports.inventoryIdsOf)(seen), hash })
|
|
1255
|
+
: undefined,
|
|
639
1256
|
}),
|
|
1257
|
+
});
|
|
1258
|
+
exceptions.push(...excepted.listed);
|
|
1259
|
+
return [
|
|
1260
|
+
...refused.map((detail) => ({ code: 'RENDER', target: key, detail })),
|
|
1261
|
+
...optionProblems.filter((gap) => gap.code !== 'OPTION-VALUE-MISSING').map((gap) => (Object.assign(Object.assign({}, gap), { target: key }))),
|
|
1262
|
+
...excepted.gaps,
|
|
640
1263
|
];
|
|
641
1264
|
});
|
|
642
|
-
const gaps =
|
|
643
|
-
|
|
1265
|
+
const gaps = new Map();
|
|
1266
|
+
for (const part of options.parts)
|
|
1267
|
+
gaps.set(part, yield gapsOfPart(part, []));
|
|
1268
|
+
return { gaps, exceptions, hints };
|
|
644
1269
|
}
|
|
645
1270
|
catch (error) {
|
|
646
1271
|
if (error instanceof thumb_1.ThumbUnavailableError)
|
|
@@ -651,5 +1276,5 @@ const completenessOf = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
651
1276
|
yield Promise.all([queue.close(), server.close()]);
|
|
652
1277
|
}
|
|
653
1278
|
});
|
|
654
|
-
exports.
|
|
1279
|
+
exports.partsCompletenessOf = partsCompletenessOf;
|
|
655
1280
|
//# sourceMappingURL=complete.js.map
|