stimeo-ui 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +124 -0
- package/dist/controllers/alert_dialog_controller.d.ts +2 -0
- package/dist/controllers/alert_dialog_controller.js +75 -4
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +16 -3
- package/dist/controllers/auto_submit_controller.js +94 -16
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +47 -5
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +2 -0
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +2 -0
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +2 -0
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +81 -47
- package/dist/controllers/carousel_controller.js +451 -100
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +52 -20
- package/dist/controllers/character_counter_controller.js +338 -63
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +3 -0
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +3 -0
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +2 -0
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +2 -0
- package/dist/controllers/command_palette_controller.js +75 -4
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +42 -14
- package/dist/controllers/conditional_fields_controller.js +345 -51
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +2 -0
- package/dist/controllers/confirm_controller.js +75 -4
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +2 -0
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +4 -0
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +40 -4
- package/dist/controllers/currency_input_controller.js +305 -74
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +3 -0
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.js +75 -4
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +59 -25
- package/dist/controllers/direct_upload_controller.js +212 -46
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +14 -6
- package/dist/controllers/dirty_form_controller.js +192 -29
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +2 -0
- package/dist/controllers/dismissible_controller.js +83 -18
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.js +75 -4
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.d.ts +2 -0
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +123 -27
- package/dist/controllers/file_dropzone_controller.js +386 -63
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +2 -0
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +7 -1
- package/dist/controllers/flash_controller.js +3 -1
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +2 -0
- package/dist/controllers/focus_controller.js +75 -4
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.d.ts +50 -10
- package/dist/controllers/form_field_controller.js +280 -62
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.d.ts +10 -8
- package/dist/controllers/form_validation_controller.js +208 -83
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +5 -1
- package/dist/controllers/frame_loading_controller.js +2 -1
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +2 -0
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +2 -0
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +43 -15
- package/dist/controllers/input_mask_controller.js +251 -76
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +2 -0
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +2 -0
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +2 -0
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +2 -0
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +2 -0
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +2 -0
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +52 -13
- package/dist/controllers/nested_form_controller.js +450 -42
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +2 -0
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +2 -0
- package/dist/controllers/number_input_controller.js +47 -5
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +62 -23
- package/dist/controllers/otp_controller.js +472 -114
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +2 -0
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +2 -0
- package/dist/controllers/overflow_menu_controller.js +2 -1
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +2 -0
- package/dist/controllers/pagination_controller.js +2 -1
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +2 -0
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +2 -0
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +30 -16
- package/dist/controllers/persist_controller.js +432 -122
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +3 -0
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.js +77 -4
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +2 -0
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +2 -0
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +3 -0
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +3 -0
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +2 -0
- package/dist/controllers/rating_controller.js +9 -5
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +2 -0
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +2 -0
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +2 -0
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +23 -15
- package/dist/controllers/scroll_area_controller.js +462 -162
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +2 -0
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +2 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +54 -11
- package/dist/controllers/separator_controller.js +354 -38
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.js +83 -10
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +2 -0
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +2 -0
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +2 -0
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +4 -1
- package/dist/controllers/spinner_controller.js +7 -5
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +2 -0
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +2 -0
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +2 -0
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +2 -0
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +98 -39
- package/dist/controllers/submit_once_controller.js +402 -122
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +2 -0
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +2 -0
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +31 -9
- package/dist/controllers/textarea_autosize_controller.js +131 -3
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +2 -0
- package/dist/controllers/theme_controller.js +8 -6
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +2 -0
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +4 -0
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +2 -0
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +2 -0
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +2 -0
- package/dist/controllers/tree_view_controller.js +2 -1
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +4293 -1447
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +115 -4
- package/dist/inspector/cli.js +195 -15
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +257 -47
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +11 -11
- package/dist/inspector/manifest.json +615 -66
- package/dist/positioning/index.d.ts +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,12 +1,72 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import process2 from 'process';
|
|
3
|
-
import { statSync, readdirSync
|
|
3
|
+
import { readFileSync, statSync, readdirSync } from 'fs';
|
|
4
4
|
import { relative, join, dirname, extname } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { createInterface } from 'readline';
|
|
7
7
|
|
|
8
8
|
// src/inspector/cli_bin.ts
|
|
9
9
|
|
|
10
|
+
// src/inspector/announcer_rules.ts
|
|
11
|
+
var ANNOUNCER_IDENTIFIER = "stimeo--announcer";
|
|
12
|
+
var ANNOUNCING_CONTROLLERS = [
|
|
13
|
+
"stimeo--auto-submit",
|
|
14
|
+
"stimeo--character-counter",
|
|
15
|
+
"stimeo--countdown",
|
|
16
|
+
"stimeo--direct-upload",
|
|
17
|
+
"stimeo--empty-state",
|
|
18
|
+
"stimeo--file-dropzone",
|
|
19
|
+
"stimeo--flash",
|
|
20
|
+
"stimeo--form-field",
|
|
21
|
+
"stimeo--frame-loading",
|
|
22
|
+
"stimeo--meter",
|
|
23
|
+
"stimeo--multi-select",
|
|
24
|
+
"stimeo--nested-form",
|
|
25
|
+
"stimeo--network-status",
|
|
26
|
+
"stimeo--progress",
|
|
27
|
+
"stimeo--skeleton",
|
|
28
|
+
"stimeo--spinner",
|
|
29
|
+
"stimeo--submit-once",
|
|
30
|
+
"stimeo--tags-input"
|
|
31
|
+
];
|
|
32
|
+
function positionAt(source, index) {
|
|
33
|
+
const before = source.slice(0, index);
|
|
34
|
+
const line = before.split("\n").length;
|
|
35
|
+
const lastBreak = before.lastIndexOf("\n");
|
|
36
|
+
return { line, column: index - lastBreak };
|
|
37
|
+
}
|
|
38
|
+
function firstAnnouncingHit(scanned) {
|
|
39
|
+
let best = null;
|
|
40
|
+
let bestIndex = Number.POSITIVE_INFINITY;
|
|
41
|
+
for (const identifier of ANNOUNCING_CONTROLLERS) {
|
|
42
|
+
const index = scanned.source.indexOf(identifier);
|
|
43
|
+
if (index === -1 || index >= bestIndex) continue;
|
|
44
|
+
bestIndex = index;
|
|
45
|
+
best = { file: scanned.file, identifier, ...positionAt(scanned.source, index) };
|
|
46
|
+
}
|
|
47
|
+
return best;
|
|
48
|
+
}
|
|
49
|
+
function announcerSeatingDiagnostic(sources) {
|
|
50
|
+
let speaker = null;
|
|
51
|
+
for (const scanned of sources) {
|
|
52
|
+
if (scanned.source.includes(ANNOUNCER_IDENTIFIER)) return null;
|
|
53
|
+
if (!speaker) speaker = firstAnnouncingHit(scanned);
|
|
54
|
+
}
|
|
55
|
+
if (!speaker) return null;
|
|
56
|
+
return {
|
|
57
|
+
file: speaker.file,
|
|
58
|
+
diagnostic: {
|
|
59
|
+
code: "missing-announcer",
|
|
60
|
+
severity: "warning",
|
|
61
|
+
message: `"${speaker.identifier}" speaks through the shared announcer, and none of the scanned files seats one.`,
|
|
62
|
+
line: speaker.line,
|
|
63
|
+
column: speaker.column,
|
|
64
|
+
length: speaker.identifier.length,
|
|
65
|
+
suggestion: `Add an element with data-controller="${ANNOUNCER_IDENTIFIER}" (and an assertive/polite target) to the layout, or include the layout in the checked paths.`
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
10
70
|
// src/inspector/html_parser.ts
|
|
11
71
|
var ERB_ELEMENT_TAG = "#erb";
|
|
12
72
|
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
@@ -932,10 +992,16 @@ function actionDescriptors(dataActionValue) {
|
|
|
932
992
|
if (hash === -1) continue;
|
|
933
993
|
let lhs = descriptor.slice(0, hash);
|
|
934
994
|
const arrow = lhs.indexOf("->");
|
|
935
|
-
|
|
995
|
+
let eventType = "";
|
|
996
|
+
if (arrow !== -1) {
|
|
997
|
+
eventType = lhs.slice(0, arrow);
|
|
998
|
+
lhs = lhs.slice(arrow + 2);
|
|
999
|
+
const at = eventType.indexOf("@");
|
|
1000
|
+
if (at !== -1) eventType = eventType.slice(0, at);
|
|
1001
|
+
}
|
|
936
1002
|
if (!lhs.startsWith(NAMESPACE_PREFIX)) continue;
|
|
937
1003
|
const method = /^[a-zA-Z_$][\w$]*/.exec(descriptor.slice(hash + 1))?.[0] ?? "";
|
|
938
|
-
descriptors.push({ identifier: lhs, method });
|
|
1004
|
+
descriptors.push({ identifier: lhs, method, eventType });
|
|
939
1005
|
}
|
|
940
1006
|
return descriptors;
|
|
941
1007
|
}
|
|
@@ -954,6 +1020,8 @@ var DIAGNOSTIC_CODES = [
|
|
|
954
1020
|
"orphan-target",
|
|
955
1021
|
"missing-required-target",
|
|
956
1022
|
"missing-conditional-target",
|
|
1023
|
+
"missing-required-action",
|
|
1024
|
+
"missing-action-completion",
|
|
957
1025
|
"missing-aria",
|
|
958
1026
|
"invalid-aria-value",
|
|
959
1027
|
"keyboard-inaccessible",
|
|
@@ -965,6 +1033,7 @@ var DIAGNOSTIC_CODES = [
|
|
|
965
1033
|
"undeclared-target",
|
|
966
1034
|
"cardinality-violation",
|
|
967
1035
|
"forbidden-aria",
|
|
1036
|
+
"missing-announcer",
|
|
968
1037
|
"unknown-ignore-code"
|
|
969
1038
|
];
|
|
970
1039
|
|
|
@@ -975,6 +1044,19 @@ function isInside(node, ancestor) {
|
|
|
975
1044
|
}
|
|
976
1045
|
return false;
|
|
977
1046
|
}
|
|
1047
|
+
function matchesHost(node, selector) {
|
|
1048
|
+
if (node.tag !== selector.tag) return false;
|
|
1049
|
+
if (!selector.attr) return true;
|
|
1050
|
+
const attr = node.attrs.find((candidate) => candidate.name === selector.attr);
|
|
1051
|
+
return attr !== void 0 && (selector.values ?? []).includes(attr.value.trim().toLowerCase());
|
|
1052
|
+
}
|
|
1053
|
+
function nearestHost(node, selectors) {
|
|
1054
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
1055
|
+
if (current.origin !== "markup" || current.opaque) return void 0;
|
|
1056
|
+
if (selectors.some((selector) => matchesHost(current, selector))) return current;
|
|
1057
|
+
}
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
978
1060
|
function checkSource(source, manifest) {
|
|
979
1061
|
const diagnostics = [];
|
|
980
1062
|
const tree = parseHtml(neutralizeErb(source), erbElements(source));
|
|
@@ -993,7 +1075,9 @@ function checkSource(source, manifest) {
|
|
|
993
1075
|
(a) => a.name === `data-${identifier}-${dasherize(when.value)}-value`
|
|
994
1076
|
);
|
|
995
1077
|
if (hostAttr && isDynamicValue(hostAttr)) return null;
|
|
996
|
-
|
|
1078
|
+
const raw = hostAttr ? hostAttr.value : when.default;
|
|
1079
|
+
const effective = when.type === "boolean" ? String(!(raw === "0" || raw.toLowerCase() === "false")) : raw;
|
|
1080
|
+
return when.equals.includes(effective);
|
|
997
1081
|
};
|
|
998
1082
|
const isContentEditable2 = (element) => {
|
|
999
1083
|
let current = element;
|
|
@@ -1066,6 +1150,17 @@ function checkSource(source, manifest) {
|
|
|
1066
1150
|
const scopes = [];
|
|
1067
1151
|
const presence = /* @__PURE__ */ new Map();
|
|
1068
1152
|
const targetNodes = /* @__PURE__ */ new Map();
|
|
1153
|
+
const wiredActions = /* @__PURE__ */ new Map();
|
|
1154
|
+
const recordAction = (owner, identifier, element, method, eventType) => {
|
|
1155
|
+
let byId = wiredActions.get(owner);
|
|
1156
|
+
if (!byId) {
|
|
1157
|
+
byId = /* @__PURE__ */ new Map();
|
|
1158
|
+
wiredActions.set(owner, byId);
|
|
1159
|
+
}
|
|
1160
|
+
const found = byId.get(identifier);
|
|
1161
|
+
if (found) found.push({ element, method, eventType });
|
|
1162
|
+
else byId.set(identifier, [{ element, method, eventType }]);
|
|
1163
|
+
};
|
|
1069
1164
|
const recordPresence = (owner, identifier, target) => {
|
|
1070
1165
|
let byId = presence.get(owner);
|
|
1071
1166
|
if (!byId) {
|
|
@@ -1097,8 +1192,9 @@ function checkSource(source, manifest) {
|
|
|
1097
1192
|
const contentHolds = (scope, identifier, element, condition) => {
|
|
1098
1193
|
if (!condition) return true;
|
|
1099
1194
|
const nodes = targetNodes.get(scope)?.get(identifier)?.get(condition.target) ?? [];
|
|
1195
|
+
const container = condition.within === "scope" ? scope : element;
|
|
1100
1196
|
let held = 0;
|
|
1101
|
-
for (const node of nodes) if (isWithin(node,
|
|
1197
|
+
for (const node of nodes) if (isWithin(node, container)) held += 1;
|
|
1102
1198
|
if (condition.min !== void 0 && held < condition.min) return false;
|
|
1103
1199
|
if (condition.max !== void 0 && held > condition.max) return false;
|
|
1104
1200
|
return true;
|
|
@@ -1274,11 +1370,12 @@ function checkSource(source, manifest) {
|
|
|
1274
1370
|
(candidate) => dasherize(candidate.value) === parsed.valueToken
|
|
1275
1371
|
);
|
|
1276
1372
|
if (constraint && !valueSatisfiesConstraint(attr.value, constraint)) {
|
|
1373
|
+
const literal = constraint.type === "string" ? attr.value : attr.value.trim();
|
|
1277
1374
|
report(
|
|
1278
1375
|
node,
|
|
1279
1376
|
"invalid-value",
|
|
1280
1377
|
"error",
|
|
1281
|
-
`Invalid value "${
|
|
1378
|
+
`Invalid value "${literal}" for "${parsed.identifier}.${parsed.valueToken}". Expected ${describeValueConstraint(constraint)}.`,
|
|
1282
1379
|
attr,
|
|
1283
1380
|
constraint.suggestion
|
|
1284
1381
|
);
|
|
@@ -1289,8 +1386,12 @@ function checkSource(source, manifest) {
|
|
|
1289
1386
|
}
|
|
1290
1387
|
}
|
|
1291
1388
|
if (attr.name === "data-action") {
|
|
1292
|
-
for (const { identifier, method } of actionDescriptors(attr.value)) {
|
|
1389
|
+
for (const { identifier, method, eventType } of actionDescriptors(attr.value)) {
|
|
1293
1390
|
const controller = known[identifier];
|
|
1391
|
+
if (controller && method.length > 0) {
|
|
1392
|
+
const owner = findOwner(node, identifier);
|
|
1393
|
+
if (owner) recordAction(owner, identifier, node, method, eventType);
|
|
1394
|
+
}
|
|
1294
1395
|
if (!controller) {
|
|
1295
1396
|
const best = nearestName(identifier, knownIdentifiers);
|
|
1296
1397
|
report(
|
|
@@ -1374,19 +1475,87 @@ function checkSource(source, manifest) {
|
|
|
1374
1475
|
if (!present.has(rule.whenPresent)) continue;
|
|
1375
1476
|
const nodesOf = (target) => targetNodes.get(node)?.get(identifier)?.get(target) ?? [];
|
|
1376
1477
|
const owners = nodesOf(rule.whenPresent);
|
|
1377
|
-
const
|
|
1478
|
+
const hosts = rule.requireSameHost;
|
|
1479
|
+
const sharesHost = (target, selectors) => {
|
|
1480
|
+
const ownerHosts = owners.map((owner) => nearestHost(owner, selectors));
|
|
1481
|
+
if (ownerHosts.some((host) => !host)) return true;
|
|
1482
|
+
const candidates = nodesOf(target);
|
|
1483
|
+
if (candidates.length === 0) return false;
|
|
1484
|
+
return candidates.some((el) => {
|
|
1485
|
+
const host = nearestHost(el, selectors);
|
|
1486
|
+
return host ? ownerHosts.includes(host) : true;
|
|
1487
|
+
});
|
|
1488
|
+
};
|
|
1489
|
+
const satisfied = (target) => {
|
|
1490
|
+
if (hosts) return sharesHost(target, hosts);
|
|
1491
|
+
if (rule.requireInside)
|
|
1492
|
+
return nodesOf(target).some((el) => owners.some((owner) => isInside(el, owner)));
|
|
1493
|
+
return present.has(target);
|
|
1494
|
+
};
|
|
1378
1495
|
const missing = rule.require.filter((target) => !satisfied(target));
|
|
1379
1496
|
if (missing.length === 0) continue;
|
|
1380
1497
|
report(
|
|
1381
1498
|
node,
|
|
1382
1499
|
"missing-conditional-target",
|
|
1383
1500
|
complete ? "error" : "warning",
|
|
1384
|
-
rule.requireInside ? `"${identifier}" requires ${list(missing)} inside its "${rule.whenPresent}".` : `"${identifier}" has a "${rule.whenPresent}" target, which also requires ${list(missing)}.`,
|
|
1501
|
+
hosts ? `"${identifier}" requires ${list(missing)} in the same ${rule.hostLabel ?? "element"} as its "${rule.whenPresent}".` : rule.requireInside ? `"${identifier}" requires ${list(missing)} inside its "${rule.whenPresent}".` : `"${identifier}" has a "${rule.whenPresent}" target, which also requires ${list(missing)}.`,
|
|
1385
1502
|
node,
|
|
1386
1503
|
complete ? rule.suggestion : UNRESOLVED_TARGET_HINT
|
|
1387
1504
|
);
|
|
1388
1505
|
}
|
|
1389
1506
|
}
|
|
1507
|
+
for (const { node, identifier } of scopes) {
|
|
1508
|
+
const controller = known[identifier];
|
|
1509
|
+
if (!controller) continue;
|
|
1510
|
+
const wired = wiredActions.get(node)?.get(identifier) ?? [];
|
|
1511
|
+
for (const rule of controller.requiredActions) {
|
|
1512
|
+
if (conditionHolds(node, identifier, rule.when) !== true) continue;
|
|
1513
|
+
const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
|
|
1514
|
+
for (const element of elements) {
|
|
1515
|
+
const actionAttr = element.attrs.find((attr) => attr.name === "data-action");
|
|
1516
|
+
if (element.opaque || actionAttr && isDynamicValue(actionAttr)) continue;
|
|
1517
|
+
const satisfied = wired.some(
|
|
1518
|
+
(action) => action.element === element && action.method === rule.action && rule.eventTypes.includes(action.eventType)
|
|
1519
|
+
);
|
|
1520
|
+
if (satisfied) continue;
|
|
1521
|
+
const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
|
|
1522
|
+
report(
|
|
1523
|
+
element,
|
|
1524
|
+
"missing-required-action",
|
|
1525
|
+
"error",
|
|
1526
|
+
`The ${where} of "${identifier}" requires action "${rule.action}" on ${list(rule.eventTypes)} in this configuration.`,
|
|
1527
|
+
element,
|
|
1528
|
+
rule.suggestion
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
for (const { node, identifier } of scopes) {
|
|
1534
|
+
const controller = known[identifier];
|
|
1535
|
+
if (!controller) continue;
|
|
1536
|
+
const wired = wiredActions.get(node)?.get(identifier) ?? [];
|
|
1537
|
+
if (wired.length === 0) continue;
|
|
1538
|
+
for (const rule of controller.actionCompletion) {
|
|
1539
|
+
const opens = wired.some(
|
|
1540
|
+
(action) => action.method === rule.opens && rule.whenTriggeredBy.includes(action.eventType)
|
|
1541
|
+
);
|
|
1542
|
+
if (!opens) continue;
|
|
1543
|
+
if (wired.some((action) => rule.closedBy.includes(action.method))) continue;
|
|
1544
|
+
if (rule.escapeValue) {
|
|
1545
|
+
const attrName = `data-${identifier}-${dasherize(rule.escapeValue)}-value`;
|
|
1546
|
+
const authored = node.attrs.find((attr) => attr.name === attrName);
|
|
1547
|
+
if (authored && (isDynamicValue(authored) || Number(authored.value.trim()) !== 0)) continue;
|
|
1548
|
+
}
|
|
1549
|
+
report(
|
|
1550
|
+
node,
|
|
1551
|
+
"missing-action-completion",
|
|
1552
|
+
"warning",
|
|
1553
|
+
`"${identifier}" opens a state with "${rule.opens}" here, and nothing in this scope closes it.`,
|
|
1554
|
+
node,
|
|
1555
|
+
rule.suggestion
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1390
1559
|
for (const { node, identifier } of scopes) {
|
|
1391
1560
|
const controller = known[identifier];
|
|
1392
1561
|
if (!controller) continue;
|
|
@@ -1689,6 +1858,7 @@ function checkSource(source, manifest) {
|
|
|
1689
1858
|
return diagnostics;
|
|
1690
1859
|
}
|
|
1691
1860
|
function valueSatisfiesConstraint(raw, constraint) {
|
|
1861
|
+
if (constraint.type === "string") return constraint.allowedValues.includes(raw);
|
|
1692
1862
|
const value = decodeNumericValue(raw);
|
|
1693
1863
|
if (Number.isNaN(value)) return false;
|
|
1694
1864
|
if (constraint.finite && !Number.isFinite(value)) return false;
|
|
@@ -1700,6 +1870,7 @@ function decodeNumericValue(raw) {
|
|
|
1700
1870
|
return Number(raw.replace(/_/g, ""));
|
|
1701
1871
|
}
|
|
1702
1872
|
function describeValueConstraint(constraint) {
|
|
1873
|
+
if (constraint.type === "string") return `one of ${list(constraint.allowedValues)}`;
|
|
1703
1874
|
const parts = [constraint.finite ? "a finite number" : "a number"];
|
|
1704
1875
|
if (constraint.greaterThan !== void 0) {
|
|
1705
1876
|
parts.push(`greater than ${constraint.greaterThan}`);
|
|
@@ -1954,6 +2125,8 @@ var CONTROLLER_ARRAY_FIELDS = [
|
|
|
1954
2125
|
"events",
|
|
1955
2126
|
"requiredTargets",
|
|
1956
2127
|
"conditionalTargets",
|
|
2128
|
+
"requiredActions",
|
|
2129
|
+
"actionCompletion",
|
|
1957
2130
|
"a11y",
|
|
1958
2131
|
"keyboard",
|
|
1959
2132
|
"hosts",
|
|
@@ -2016,10 +2189,16 @@ function collectFiles(target, out) {
|
|
|
2016
2189
|
}
|
|
2017
2190
|
if (isCheckableFile(target)) out.push(target);
|
|
2018
2191
|
}
|
|
2019
|
-
function checkFile(file, manifest) {
|
|
2020
|
-
const source = readFileSync(file, "utf8");
|
|
2192
|
+
function checkFile(file, source, manifest) {
|
|
2021
2193
|
return { file, diagnostics: checkSource(source, manifest) };
|
|
2022
2194
|
}
|
|
2195
|
+
function withAnnouncerSeating(reports, sources) {
|
|
2196
|
+
const seating = announcerSeatingDiagnostic(sources);
|
|
2197
|
+
if (!seating) return [...reports];
|
|
2198
|
+
return reports.map(
|
|
2199
|
+
(report) => report.file === seating.file ? { file: report.file, diagnostics: [...report.diagnostics, seating.diagnostic] } : report
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2023
2202
|
function formatDiagnostic(d) {
|
|
2024
2203
|
const where = `${d.line}:${d.column}`.padEnd(7);
|
|
2025
2204
|
const head = ` ${where} ${d.severity.padEnd(7)} ${d.message} [${d.code}]`;
|
|
@@ -2152,13 +2331,14 @@ ${USAGE}`);
|
|
|
2152
2331
|
const manifest = load();
|
|
2153
2332
|
const cwd = process2.cwd();
|
|
2154
2333
|
const reports = [];
|
|
2334
|
+
const sources = [];
|
|
2155
2335
|
for (const file of files.sort()) {
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2336
|
+
const path = relative(cwd, file) || file;
|
|
2337
|
+
const source = readFileSync(file, "utf8");
|
|
2338
|
+
sources.push({ file: path, source });
|
|
2339
|
+
reports.push(checkFile(path, source, manifest));
|
|
2160
2340
|
}
|
|
2161
|
-
const summary = buildCheckReport(reports, files.length);
|
|
2341
|
+
const summary = buildCheckReport(withAnnouncerSeating(reports, sources), files.length);
|
|
2162
2342
|
if (json) {
|
|
2163
2343
|
write(JSON.stringify(summary, null, 2));
|
|
2164
2344
|
return summary.errorCount > 0 ? 1 : 0;
|
|
@@ -2388,6 +2568,17 @@ var EXAMPLE_RESULT_SCHEMA = {
|
|
|
2388
2568
|
},
|
|
2389
2569
|
required: ["id", "file", "source", "guidance"]
|
|
2390
2570
|
};
|
|
2571
|
+
var VALUE_CONDITION_SCHEMA = {
|
|
2572
|
+
type: "object",
|
|
2573
|
+
description: "Own-value condition arming a rule; absent means unconditional.",
|
|
2574
|
+
properties: {
|
|
2575
|
+
value: { type: "string" },
|
|
2576
|
+
type: { type: "string", enum: ["boolean", "string"] },
|
|
2577
|
+
equals: STRING_ARRAY,
|
|
2578
|
+
default: { type: "string" }
|
|
2579
|
+
},
|
|
2580
|
+
required: ["value", "type", "equals", "default"]
|
|
2581
|
+
};
|
|
2391
2582
|
var CONTENT_CONDITION_SCHEMA = {
|
|
2392
2583
|
type: "object",
|
|
2393
2584
|
description: "Condition on the element's own contents arming the rule; absent means it does not depend on them. Evaluated per element, unlike the scope-level `when`.",
|
|
@@ -2436,9 +2627,14 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2436
2627
|
type: "object",
|
|
2437
2628
|
properties: {
|
|
2438
2629
|
value: { type: "string", description: "Camel-case declared Value name." },
|
|
2439
|
-
type: { type: "string", enum: ["number"] },
|
|
2630
|
+
type: { type: "string", enum: ["number", "string"] },
|
|
2440
2631
|
finite: { type: "boolean" },
|
|
2441
2632
|
greaterThan: { type: "number" },
|
|
2633
|
+
integer: { type: "boolean" },
|
|
2634
|
+
allowedValues: {
|
|
2635
|
+
...STRING_ARRAY,
|
|
2636
|
+
description: "Accepted literals for a String Value constraint."
|
|
2637
|
+
},
|
|
2442
2638
|
suggestion: { type: "string" }
|
|
2443
2639
|
},
|
|
2444
2640
|
required: ["value", "type", "suggestion"]
|
|
@@ -2479,6 +2675,45 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2479
2675
|
required: ["whenPresent", "require", "suggestion"]
|
|
2480
2676
|
}
|
|
2481
2677
|
},
|
|
2678
|
+
requiredActions: {
|
|
2679
|
+
type: "array",
|
|
2680
|
+
description: "Action bindings required on a particular element in one configured Value state.",
|
|
2681
|
+
items: {
|
|
2682
|
+
type: "object",
|
|
2683
|
+
properties: {
|
|
2684
|
+
target: { type: "string", description: 'Target name; "" means the scope element.' },
|
|
2685
|
+
action: { type: "string", description: "Public action method that must be wired." },
|
|
2686
|
+
eventTypes: {
|
|
2687
|
+
...STRING_ARRAY,
|
|
2688
|
+
description: "Event types that make the binding usable for this contract."
|
|
2689
|
+
},
|
|
2690
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2691
|
+
suggestion: { type: "string" }
|
|
2692
|
+
},
|
|
2693
|
+
required: ["target", "action", "eventTypes", "suggestion"]
|
|
2694
|
+
}
|
|
2695
|
+
},
|
|
2696
|
+
actionCompletion: {
|
|
2697
|
+
type: "array",
|
|
2698
|
+
description: "States an action opens that the consumer's own wiring has to close. Wiring the opening action without any closing one leaves the controller busy for good, and nothing else in the markup reveals it.",
|
|
2699
|
+
items: {
|
|
2700
|
+
type: "object",
|
|
2701
|
+
properties: {
|
|
2702
|
+
opens: { type: "string", description: "Action that opens the state." },
|
|
2703
|
+
whenTriggeredBy: {
|
|
2704
|
+
...STRING_ARRAY,
|
|
2705
|
+
description: "Event types for that action the controller cannot complete itself."
|
|
2706
|
+
},
|
|
2707
|
+
closedBy: { ...STRING_ARRAY, description: "Any one of these closes it again." },
|
|
2708
|
+
escapeValue: {
|
|
2709
|
+
type: "string",
|
|
2710
|
+
description: "Value whose non-zero number supplies an automatic exit instead."
|
|
2711
|
+
},
|
|
2712
|
+
suggestion: { type: "string" }
|
|
2713
|
+
},
|
|
2714
|
+
required: ["opens", "whenTriggeredBy", "closedBy", "suggestion"]
|
|
2715
|
+
}
|
|
2716
|
+
},
|
|
2482
2717
|
a11y: {
|
|
2483
2718
|
type: "array",
|
|
2484
2719
|
description: "Author-supplied ARIA the controller relies on but never sets itself.",
|
|
@@ -2500,16 +2735,7 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2500
2735
|
required: ["attrs"]
|
|
2501
2736
|
}
|
|
2502
2737
|
},
|
|
2503
|
-
when:
|
|
2504
|
-
type: "object",
|
|
2505
|
-
description: "Own-value condition arming the requirement; absent means unconditional. Outside the named configuration the ARIA is not optional but wrong.",
|
|
2506
|
-
properties: {
|
|
2507
|
-
value: { type: "string" },
|
|
2508
|
-
equals: STRING_ARRAY,
|
|
2509
|
-
default: { type: "string" }
|
|
2510
|
-
},
|
|
2511
|
-
required: ["value", "equals", "default"]
|
|
2512
|
-
},
|
|
2738
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2513
2739
|
whenContains: CONTENT_CONDITION_SCHEMA,
|
|
2514
2740
|
whenElement: ELEMENT_CONDITION_SCHEMA,
|
|
2515
2741
|
whenDocument: DOCUMENT_CONDITION_SCHEMA,
|
|
@@ -2593,16 +2819,7 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2593
2819
|
description: "Check the scope element when the target is absent."
|
|
2594
2820
|
},
|
|
2595
2821
|
coController: { type: "string", description: "Companion controller identifier." },
|
|
2596
|
-
when:
|
|
2597
|
-
type: "object",
|
|
2598
|
-
description: "Host-value condition arming the rule; absent means unconditional.",
|
|
2599
|
-
properties: {
|
|
2600
|
-
value: { type: "string" },
|
|
2601
|
-
equals: STRING_ARRAY,
|
|
2602
|
-
default: { type: "string" }
|
|
2603
|
-
},
|
|
2604
|
-
required: ["value", "equals", "default"]
|
|
2605
|
-
},
|
|
2822
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2606
2823
|
require: {
|
|
2607
2824
|
type: "object",
|
|
2608
2825
|
description: "Companion-value requirement (first allowed value is canonical).",
|
|
@@ -2666,16 +2883,7 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2666
2883
|
values: { ...STRING_ARRAY, description: "Restrict the attribute to these values." },
|
|
2667
2884
|
min: { type: "integer", description: "Smallest permitted count (inclusive)." },
|
|
2668
2885
|
max: { type: "integer", description: "Largest permitted count (inclusive)." },
|
|
2669
|
-
when:
|
|
2670
|
-
type: "object",
|
|
2671
|
-
description: "Own-value condition arming the bound; absent means unconditional.",
|
|
2672
|
-
properties: {
|
|
2673
|
-
value: { type: "string" },
|
|
2674
|
-
equals: STRING_ARRAY,
|
|
2675
|
-
default: { type: "string" }
|
|
2676
|
-
},
|
|
2677
|
-
required: ["value", "equals", "default"]
|
|
2678
|
-
},
|
|
2886
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2679
2887
|
suggestion: { type: "string" }
|
|
2680
2888
|
},
|
|
2681
2889
|
required: ["within", "target", "suggestion"]
|
|
@@ -2707,6 +2915,8 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2707
2915
|
"events",
|
|
2708
2916
|
"requiredTargets",
|
|
2709
2917
|
"conditionalTargets",
|
|
2918
|
+
"requiredActions",
|
|
2919
|
+
"actionCompletion",
|
|
2710
2920
|
"a11y",
|
|
2711
2921
|
"keyboard",
|
|
2712
2922
|
"hosts",
|