stimeo-ui 0.5.0 → 0.7.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 +178 -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/aspect_ratio_controller.d.ts +1 -3
- package/dist/controllers/aspect_ratio_controller.js +19 -11
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +2 -0
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.d.ts +36 -15
- package/dist/controllers/avatar_controller.js +237 -40
- 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 +12 -3
- package/dist/controllers/carousel_controller.js +85 -9
- 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 +34 -5
- package/dist/controllers/checkbox_controller.js +136 -25
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +10 -1
- package/dist/controllers/color_picker_controller.js +35 -9
- 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 +2 -0
- 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/date_range_picker_controller.d.ts +23 -4
- package/dist/controllers/date_range_picker_controller.js +157 -30
- package/dist/controllers/date_range_picker_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 +56 -24
- package/dist/controllers/direct_upload_controller.js +201 -45
- 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 +9 -1
- package/dist/controllers/file_dropzone_controller.js +26 -3
- 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 +4 -0
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +4 -3
- 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 +2 -0
- 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/hover_card_controller.d.ts +2 -2
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +5 -3
- package/dist/controllers/idle_controller.js +27 -5
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +2 -0
- 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/menubar_controller.js +5 -3
- package/dist/controllers/menubar_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 +48 -12
- package/dist/controllers/multi_select_controller.js +460 -151
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +2 -0
- 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 +26 -6
- package/dist/controllers/number_input_controller.js +317 -51
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +3 -0
- 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 +6 -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 +35 -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 +5 -3
- package/dist/controllers/password_strength_controller.js +20 -2
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +33 -19
- package/dist/controllers/persist_controller.js +449 -120
- 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.d.ts +2 -2
- package/dist/controllers/popover_controller.js +77 -4
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +3 -2
- 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 +44 -15
- package/dist/controllers/radio_group_controller.js +540 -56
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +38 -31
- package/dist/controllers/rating_controller.js +276 -89
- 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 +33 -0
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +6 -0
- package/dist/controllers/roving_controller.js +60 -5
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +25 -11
- package/dist/controllers/scroll_area_controller.js +557 -125
- 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 +33 -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 +2 -0
- 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 +94 -38
- package/dist/controllers/submit_once_controller.js +399 -121
- 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 +43 -11
- package/dist/controllers/tags_input_controller.js +356 -120
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +2 -0
- 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 +44 -13
- package/dist/controllers/time_picker_controller.js +296 -107
- 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 +41 -13
- package/dist/controllers/toggle_group_controller.js +378 -55
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +5 -3
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +2 -2
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +4 -3
- 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 +7 -4
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +4963 -1581
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +123 -6
- package/dist/inspector/cli.js +212 -18
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +274 -50
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +15 -15
- package/dist/inspector/manifest.json +602 -80
- package/dist/positioning/index.d.ts +4 -2
- package/dist/positioning/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,71 @@
|
|
|
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--flash",
|
|
19
|
+
"stimeo--form-field",
|
|
20
|
+
"stimeo--frame-loading",
|
|
21
|
+
"stimeo--meter",
|
|
22
|
+
"stimeo--multi-select",
|
|
23
|
+
"stimeo--nested-form",
|
|
24
|
+
"stimeo--network-status",
|
|
25
|
+
"stimeo--progress",
|
|
26
|
+
"stimeo--skeleton",
|
|
27
|
+
"stimeo--spinner",
|
|
28
|
+
"stimeo--submit-once",
|
|
29
|
+
"stimeo--tags-input"
|
|
30
|
+
];
|
|
31
|
+
function positionAt(source, index) {
|
|
32
|
+
const before = source.slice(0, index);
|
|
33
|
+
const line = before.split("\n").length;
|
|
34
|
+
const lastBreak = before.lastIndexOf("\n");
|
|
35
|
+
return { line, column: index - lastBreak };
|
|
36
|
+
}
|
|
37
|
+
function firstAnnouncingHit(scanned) {
|
|
38
|
+
let best = null;
|
|
39
|
+
let bestIndex = Number.POSITIVE_INFINITY;
|
|
40
|
+
for (const identifier of ANNOUNCING_CONTROLLERS) {
|
|
41
|
+
const index = scanned.source.indexOf(identifier);
|
|
42
|
+
if (index === -1 || index >= bestIndex) continue;
|
|
43
|
+
bestIndex = index;
|
|
44
|
+
best = { file: scanned.file, identifier, ...positionAt(scanned.source, index) };
|
|
45
|
+
}
|
|
46
|
+
return best;
|
|
47
|
+
}
|
|
48
|
+
function announcerSeatingDiagnostic(sources) {
|
|
49
|
+
let speaker = null;
|
|
50
|
+
for (const scanned of sources) {
|
|
51
|
+
if (scanned.source.includes(ANNOUNCER_IDENTIFIER)) return null;
|
|
52
|
+
if (!speaker) speaker = firstAnnouncingHit(scanned);
|
|
53
|
+
}
|
|
54
|
+
if (!speaker) return null;
|
|
55
|
+
return {
|
|
56
|
+
file: speaker.file,
|
|
57
|
+
diagnostic: {
|
|
58
|
+
code: "missing-announcer",
|
|
59
|
+
severity: "warning",
|
|
60
|
+
message: `"${speaker.identifier}" speaks through the shared announcer, and none of the scanned files seats one.`,
|
|
61
|
+
line: speaker.line,
|
|
62
|
+
column: speaker.column,
|
|
63
|
+
length: speaker.identifier.length,
|
|
64
|
+
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.`
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
10
69
|
// src/inspector/html_parser.ts
|
|
11
70
|
var ERB_ELEMENT_TAG = "#erb";
|
|
12
71
|
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
@@ -932,10 +991,16 @@ function actionDescriptors(dataActionValue) {
|
|
|
932
991
|
if (hash === -1) continue;
|
|
933
992
|
let lhs = descriptor.slice(0, hash);
|
|
934
993
|
const arrow = lhs.indexOf("->");
|
|
935
|
-
|
|
994
|
+
let eventType = "";
|
|
995
|
+
if (arrow !== -1) {
|
|
996
|
+
eventType = lhs.slice(0, arrow);
|
|
997
|
+
lhs = lhs.slice(arrow + 2);
|
|
998
|
+
const at = eventType.indexOf("@");
|
|
999
|
+
if (at !== -1) eventType = eventType.slice(0, at);
|
|
1000
|
+
}
|
|
936
1001
|
if (!lhs.startsWith(NAMESPACE_PREFIX)) continue;
|
|
937
1002
|
const method = /^[a-zA-Z_$][\w$]*/.exec(descriptor.slice(hash + 1))?.[0] ?? "";
|
|
938
|
-
descriptors.push({ identifier: lhs, method });
|
|
1003
|
+
descriptors.push({ identifier: lhs, method, eventType });
|
|
939
1004
|
}
|
|
940
1005
|
return descriptors;
|
|
941
1006
|
}
|
|
@@ -954,6 +1019,8 @@ var DIAGNOSTIC_CODES = [
|
|
|
954
1019
|
"orphan-target",
|
|
955
1020
|
"missing-required-target",
|
|
956
1021
|
"missing-conditional-target",
|
|
1022
|
+
"missing-required-action",
|
|
1023
|
+
"missing-action-completion",
|
|
957
1024
|
"missing-aria",
|
|
958
1025
|
"invalid-aria-value",
|
|
959
1026
|
"keyboard-inaccessible",
|
|
@@ -965,10 +1032,30 @@ var DIAGNOSTIC_CODES = [
|
|
|
965
1032
|
"undeclared-target",
|
|
966
1033
|
"cardinality-violation",
|
|
967
1034
|
"forbidden-aria",
|
|
1035
|
+
"missing-announcer",
|
|
968
1036
|
"unknown-ignore-code"
|
|
969
1037
|
];
|
|
970
1038
|
|
|
971
1039
|
// src/inspector/check.ts
|
|
1040
|
+
function isInside(node, ancestor) {
|
|
1041
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
1042
|
+
if (current === ancestor) return true;
|
|
1043
|
+
}
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
function matchesHost(node, selector) {
|
|
1047
|
+
if (node.tag !== selector.tag) return false;
|
|
1048
|
+
if (!selector.attr) return true;
|
|
1049
|
+
const attr = node.attrs.find((candidate) => candidate.name === selector.attr);
|
|
1050
|
+
return attr !== void 0 && (selector.values ?? []).includes(attr.value.trim().toLowerCase());
|
|
1051
|
+
}
|
|
1052
|
+
function nearestHost(node, selectors) {
|
|
1053
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
1054
|
+
if (current.origin !== "markup" || current.opaque) return void 0;
|
|
1055
|
+
if (selectors.some((selector) => matchesHost(current, selector))) return current;
|
|
1056
|
+
}
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
972
1059
|
function checkSource(source, manifest) {
|
|
973
1060
|
const diagnostics = [];
|
|
974
1061
|
const tree = parseHtml(neutralizeErb(source), erbElements(source));
|
|
@@ -987,7 +1074,9 @@ function checkSource(source, manifest) {
|
|
|
987
1074
|
(a) => a.name === `data-${identifier}-${dasherize(when.value)}-value`
|
|
988
1075
|
);
|
|
989
1076
|
if (hostAttr && isDynamicValue(hostAttr)) return null;
|
|
990
|
-
|
|
1077
|
+
const raw = hostAttr ? hostAttr.value : when.default;
|
|
1078
|
+
const effective = when.type === "boolean" ? String(!(raw === "0" || raw.toLowerCase() === "false")) : raw;
|
|
1079
|
+
return when.equals.includes(effective);
|
|
991
1080
|
};
|
|
992
1081
|
const isContentEditable2 = (element) => {
|
|
993
1082
|
let current = element;
|
|
@@ -1060,6 +1149,17 @@ function checkSource(source, manifest) {
|
|
|
1060
1149
|
const scopes = [];
|
|
1061
1150
|
const presence = /* @__PURE__ */ new Map();
|
|
1062
1151
|
const targetNodes = /* @__PURE__ */ new Map();
|
|
1152
|
+
const wiredActions = /* @__PURE__ */ new Map();
|
|
1153
|
+
const recordAction = (owner, identifier, element, method, eventType) => {
|
|
1154
|
+
let byId = wiredActions.get(owner);
|
|
1155
|
+
if (!byId) {
|
|
1156
|
+
byId = /* @__PURE__ */ new Map();
|
|
1157
|
+
wiredActions.set(owner, byId);
|
|
1158
|
+
}
|
|
1159
|
+
const found = byId.get(identifier);
|
|
1160
|
+
if (found) found.push({ element, method, eventType });
|
|
1161
|
+
else byId.set(identifier, [{ element, method, eventType }]);
|
|
1162
|
+
};
|
|
1063
1163
|
const recordPresence = (owner, identifier, target) => {
|
|
1064
1164
|
let byId = presence.get(owner);
|
|
1065
1165
|
if (!byId) {
|
|
@@ -1268,11 +1368,12 @@ function checkSource(source, manifest) {
|
|
|
1268
1368
|
(candidate) => dasherize(candidate.value) === parsed.valueToken
|
|
1269
1369
|
);
|
|
1270
1370
|
if (constraint && !valueSatisfiesConstraint(attr.value, constraint)) {
|
|
1371
|
+
const literal = constraint.type === "string" ? attr.value : attr.value.trim();
|
|
1271
1372
|
report(
|
|
1272
1373
|
node,
|
|
1273
1374
|
"invalid-value",
|
|
1274
1375
|
"error",
|
|
1275
|
-
`Invalid value "${
|
|
1376
|
+
`Invalid value "${literal}" for "${parsed.identifier}.${parsed.valueToken}". Expected ${describeValueConstraint(constraint)}.`,
|
|
1276
1377
|
attr,
|
|
1277
1378
|
constraint.suggestion
|
|
1278
1379
|
);
|
|
@@ -1283,8 +1384,12 @@ function checkSource(source, manifest) {
|
|
|
1283
1384
|
}
|
|
1284
1385
|
}
|
|
1285
1386
|
if (attr.name === "data-action") {
|
|
1286
|
-
for (const { identifier, method } of actionDescriptors(attr.value)) {
|
|
1387
|
+
for (const { identifier, method, eventType } of actionDescriptors(attr.value)) {
|
|
1287
1388
|
const controller = known[identifier];
|
|
1389
|
+
if (controller && method.length > 0) {
|
|
1390
|
+
const owner = findOwner(node, identifier);
|
|
1391
|
+
if (owner) recordAction(owner, identifier, node, method, eventType);
|
|
1392
|
+
}
|
|
1288
1393
|
if (!controller) {
|
|
1289
1394
|
const best = nearestName(identifier, knownIdentifiers);
|
|
1290
1395
|
report(
|
|
@@ -1366,18 +1471,89 @@ function checkSource(source, manifest) {
|
|
|
1366
1471
|
const present = presence.get(node)?.get(identifier) ?? /* @__PURE__ */ new Set();
|
|
1367
1472
|
for (const rule of controller.conditionalTargets) {
|
|
1368
1473
|
if (!present.has(rule.whenPresent)) continue;
|
|
1369
|
-
const
|
|
1474
|
+
const nodesOf = (target) => targetNodes.get(node)?.get(identifier)?.get(target) ?? [];
|
|
1475
|
+
const owners = nodesOf(rule.whenPresent);
|
|
1476
|
+
const hosts = rule.requireSameHost;
|
|
1477
|
+
const sharesHost = (target, selectors) => {
|
|
1478
|
+
const ownerHosts = owners.map((owner) => nearestHost(owner, selectors));
|
|
1479
|
+
if (ownerHosts.some((host) => !host)) return true;
|
|
1480
|
+
const candidates = nodesOf(target);
|
|
1481
|
+
if (candidates.length === 0) return false;
|
|
1482
|
+
return candidates.some((el) => {
|
|
1483
|
+
const host = nearestHost(el, selectors);
|
|
1484
|
+
return host ? ownerHosts.includes(host) : true;
|
|
1485
|
+
});
|
|
1486
|
+
};
|
|
1487
|
+
const satisfied = (target) => {
|
|
1488
|
+
if (hosts) return sharesHost(target, hosts);
|
|
1489
|
+
if (rule.requireInside)
|
|
1490
|
+
return nodesOf(target).some((el) => owners.some((owner) => isInside(el, owner)));
|
|
1491
|
+
return present.has(target);
|
|
1492
|
+
};
|
|
1493
|
+
const missing = rule.require.filter((target) => !satisfied(target));
|
|
1370
1494
|
if (missing.length === 0) continue;
|
|
1371
1495
|
report(
|
|
1372
1496
|
node,
|
|
1373
1497
|
"missing-conditional-target",
|
|
1374
1498
|
complete ? "error" : "warning",
|
|
1375
|
-
`"${identifier}" has a "${rule.whenPresent}" target, which also requires ${list(missing)}.`,
|
|
1499
|
+
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)}.`,
|
|
1376
1500
|
node,
|
|
1377
1501
|
complete ? rule.suggestion : UNRESOLVED_TARGET_HINT
|
|
1378
1502
|
);
|
|
1379
1503
|
}
|
|
1380
1504
|
}
|
|
1505
|
+
for (const { node, identifier } of scopes) {
|
|
1506
|
+
const controller = known[identifier];
|
|
1507
|
+
if (!controller) continue;
|
|
1508
|
+
const wired = wiredActions.get(node)?.get(identifier) ?? [];
|
|
1509
|
+
for (const rule of controller.requiredActions) {
|
|
1510
|
+
if (conditionHolds(node, identifier, rule.when) !== true) continue;
|
|
1511
|
+
const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
|
|
1512
|
+
for (const element of elements) {
|
|
1513
|
+
const actionAttr = element.attrs.find((attr) => attr.name === "data-action");
|
|
1514
|
+
if (element.opaque || actionAttr && isDynamicValue(actionAttr)) continue;
|
|
1515
|
+
const satisfied = wired.some(
|
|
1516
|
+
(action) => action.element === element && action.method === rule.action && rule.eventTypes.includes(action.eventType)
|
|
1517
|
+
);
|
|
1518
|
+
if (satisfied) continue;
|
|
1519
|
+
const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
|
|
1520
|
+
report(
|
|
1521
|
+
element,
|
|
1522
|
+
"missing-required-action",
|
|
1523
|
+
"error",
|
|
1524
|
+
`The ${where} of "${identifier}" requires action "${rule.action}" on ${list(rule.eventTypes)} in this configuration.`,
|
|
1525
|
+
element,
|
|
1526
|
+
rule.suggestion
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
for (const { node, identifier } of scopes) {
|
|
1532
|
+
const controller = known[identifier];
|
|
1533
|
+
if (!controller) continue;
|
|
1534
|
+
const wired = wiredActions.get(node)?.get(identifier) ?? [];
|
|
1535
|
+
if (wired.length === 0) continue;
|
|
1536
|
+
for (const rule of controller.actionCompletion) {
|
|
1537
|
+
const opens = wired.some(
|
|
1538
|
+
(action) => action.method === rule.opens && rule.whenTriggeredBy.includes(action.eventType)
|
|
1539
|
+
);
|
|
1540
|
+
if (!opens) continue;
|
|
1541
|
+
if (wired.some((action) => rule.closedBy.includes(action.method))) continue;
|
|
1542
|
+
if (rule.escapeValue) {
|
|
1543
|
+
const attrName = `data-${identifier}-${dasherize(rule.escapeValue)}-value`;
|
|
1544
|
+
const authored = node.attrs.find((attr) => attr.name === attrName);
|
|
1545
|
+
if (authored && (isDynamicValue(authored) || Number(authored.value.trim()) !== 0)) continue;
|
|
1546
|
+
}
|
|
1547
|
+
report(
|
|
1548
|
+
node,
|
|
1549
|
+
"missing-action-completion",
|
|
1550
|
+
"warning",
|
|
1551
|
+
`"${identifier}" opens a state with "${rule.opens}" here, and nothing in this scope closes it.`,
|
|
1552
|
+
node,
|
|
1553
|
+
rule.suggestion
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1381
1557
|
for (const { node, identifier } of scopes) {
|
|
1382
1558
|
const controller = known[identifier];
|
|
1383
1559
|
if (!controller) continue;
|
|
@@ -1390,7 +1566,7 @@ function checkSource(source, manifest) {
|
|
|
1390
1566
|
if (!hasReadableMarkup(el)) continue;
|
|
1391
1567
|
if (!contentHolds(node, identifier, el, req.whenContains)) continue;
|
|
1392
1568
|
if (!elementHolds(el, req.whenElement)) continue;
|
|
1393
|
-
checkA11y(report, el, identifier, req, severity);
|
|
1569
|
+
checkA11y(report, el, identifier, req, severity, isDynamicValue);
|
|
1394
1570
|
}
|
|
1395
1571
|
}
|
|
1396
1572
|
}
|
|
@@ -1680,20 +1856,24 @@ function checkSource(source, manifest) {
|
|
|
1680
1856
|
return diagnostics;
|
|
1681
1857
|
}
|
|
1682
1858
|
function valueSatisfiesConstraint(raw, constraint) {
|
|
1859
|
+
if (constraint.type === "string") return constraint.allowedValues.includes(raw);
|
|
1683
1860
|
const value = decodeNumericValue(raw);
|
|
1684
1861
|
if (Number.isNaN(value)) return false;
|
|
1685
1862
|
if (constraint.finite && !Number.isFinite(value)) return false;
|
|
1686
1863
|
if (constraint.greaterThan !== void 0 && !(value > constraint.greaterThan)) return false;
|
|
1864
|
+
if (constraint.integer && !Number.isInteger(value)) return false;
|
|
1687
1865
|
return true;
|
|
1688
1866
|
}
|
|
1689
1867
|
function decodeNumericValue(raw) {
|
|
1690
1868
|
return Number(raw.replace(/_/g, ""));
|
|
1691
1869
|
}
|
|
1692
1870
|
function describeValueConstraint(constraint) {
|
|
1871
|
+
if (constraint.type === "string") return `one of ${list(constraint.allowedValues)}`;
|
|
1693
1872
|
const parts = [constraint.finite ? "a finite number" : "a number"];
|
|
1694
1873
|
if (constraint.greaterThan !== void 0) {
|
|
1695
1874
|
parts.push(`greater than ${constraint.greaterThan}`);
|
|
1696
1875
|
}
|
|
1876
|
+
if (constraint.integer) parts.push("with no fractional part");
|
|
1697
1877
|
return parts.join(" ");
|
|
1698
1878
|
}
|
|
1699
1879
|
var UNRESOLVED_TARGET_HINT = "Part of this scope is rendered by a helper or a non-literal data: hash, so the target may exist at runtime.";
|
|
@@ -1701,7 +1881,7 @@ var UNRESOLVED_HOST_HINT = "An enclosing helper's data: option is not a literal
|
|
|
1701
1881
|
function hasReadableMarkup(el) {
|
|
1702
1882
|
return el.origin === "markup";
|
|
1703
1883
|
}
|
|
1704
|
-
function checkA11y(report, el, identifier, req, severity) {
|
|
1884
|
+
function checkA11y(report, el, identifier, req, severity, isDynamicValue) {
|
|
1705
1885
|
const groups = [
|
|
1706
1886
|
{ attrs: req.attrs, values: req.values },
|
|
1707
1887
|
...req.or ?? []
|
|
@@ -1721,9 +1901,14 @@ function checkA11y(report, el, identifier, req, severity) {
|
|
|
1721
1901
|
}
|
|
1722
1902
|
const invalid = [];
|
|
1723
1903
|
let anyPresent = false;
|
|
1904
|
+
let blank;
|
|
1724
1905
|
for (const [name, allowed] of allowedByAttr) {
|
|
1725
1906
|
const attr = el.attrs.find((a) => a.name === name);
|
|
1726
1907
|
if (!attr) continue;
|
|
1908
|
+
if (allowed.anyValue && attr.value.trim() === "" && !isDynamicValue(attr)) {
|
|
1909
|
+
blank ??= attr;
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1727
1912
|
anyPresent = true;
|
|
1728
1913
|
if (!allowed.anyValue && !allowed.values.has(attr.value.trim())) {
|
|
1729
1914
|
invalid.push({ attr, values: [...allowed.values] });
|
|
@@ -1735,8 +1920,8 @@ function checkA11y(report, el, identifier, req, severity) {
|
|
|
1735
1920
|
el,
|
|
1736
1921
|
"missing-aria",
|
|
1737
1922
|
severity,
|
|
1738
|
-
`"${identifier}" ${verb} ${describeAttrs([...allowedByAttr.keys()])} on its ${where}.`,
|
|
1739
|
-
el,
|
|
1923
|
+
blank ? `${blank.name} is empty on the ${where} of "${identifier}", which ${verb} a value there.` : `"${identifier}" ${verb} ${describeAttrs([...allowedByAttr.keys()])} on its ${where}.`,
|
|
1924
|
+
blank ?? el,
|
|
1740
1925
|
req.suggestion
|
|
1741
1926
|
);
|
|
1742
1927
|
return;
|
|
@@ -1938,6 +2123,8 @@ var CONTROLLER_ARRAY_FIELDS = [
|
|
|
1938
2123
|
"events",
|
|
1939
2124
|
"requiredTargets",
|
|
1940
2125
|
"conditionalTargets",
|
|
2126
|
+
"requiredActions",
|
|
2127
|
+
"actionCompletion",
|
|
1941
2128
|
"a11y",
|
|
1942
2129
|
"keyboard",
|
|
1943
2130
|
"hosts",
|
|
@@ -2000,10 +2187,16 @@ function collectFiles(target, out) {
|
|
|
2000
2187
|
}
|
|
2001
2188
|
if (isCheckableFile(target)) out.push(target);
|
|
2002
2189
|
}
|
|
2003
|
-
function checkFile(file, manifest) {
|
|
2004
|
-
const source = readFileSync(file, "utf8");
|
|
2190
|
+
function checkFile(file, source, manifest) {
|
|
2005
2191
|
return { file, diagnostics: checkSource(source, manifest) };
|
|
2006
2192
|
}
|
|
2193
|
+
function withAnnouncerSeating(reports, sources) {
|
|
2194
|
+
const seating = announcerSeatingDiagnostic(sources);
|
|
2195
|
+
if (!seating) return [...reports];
|
|
2196
|
+
return reports.map(
|
|
2197
|
+
(report) => report.file === seating.file ? { file: report.file, diagnostics: [...report.diagnostics, seating.diagnostic] } : report
|
|
2198
|
+
);
|
|
2199
|
+
}
|
|
2007
2200
|
function formatDiagnostic(d) {
|
|
2008
2201
|
const where = `${d.line}:${d.column}`.padEnd(7);
|
|
2009
2202
|
const head = ` ${where} ${d.severity.padEnd(7)} ${d.message} [${d.code}]`;
|
|
@@ -2136,13 +2329,14 @@ ${USAGE}`);
|
|
|
2136
2329
|
const manifest = load();
|
|
2137
2330
|
const cwd = process2.cwd();
|
|
2138
2331
|
const reports = [];
|
|
2332
|
+
const sources = [];
|
|
2139
2333
|
for (const file of files.sort()) {
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2334
|
+
const path = relative(cwd, file) || file;
|
|
2335
|
+
const source = readFileSync(file, "utf8");
|
|
2336
|
+
sources.push({ file: path, source });
|
|
2337
|
+
reports.push(checkFile(path, source, manifest));
|
|
2144
2338
|
}
|
|
2145
|
-
const summary = buildCheckReport(reports, files.length);
|
|
2339
|
+
const summary = buildCheckReport(withAnnouncerSeating(reports, sources), files.length);
|
|
2146
2340
|
if (json) {
|
|
2147
2341
|
write(JSON.stringify(summary, null, 2));
|
|
2148
2342
|
return summary.errorCount > 0 ? 1 : 0;
|
|
@@ -2372,6 +2566,17 @@ var EXAMPLE_RESULT_SCHEMA = {
|
|
|
2372
2566
|
},
|
|
2373
2567
|
required: ["id", "file", "source", "guidance"]
|
|
2374
2568
|
};
|
|
2569
|
+
var VALUE_CONDITION_SCHEMA = {
|
|
2570
|
+
type: "object",
|
|
2571
|
+
description: "Own-value condition arming a rule; absent means unconditional.",
|
|
2572
|
+
properties: {
|
|
2573
|
+
value: { type: "string" },
|
|
2574
|
+
type: { type: "string", enum: ["boolean", "string"] },
|
|
2575
|
+
equals: STRING_ARRAY,
|
|
2576
|
+
default: { type: "string" }
|
|
2577
|
+
},
|
|
2578
|
+
required: ["value", "type", "equals", "default"]
|
|
2579
|
+
};
|
|
2375
2580
|
var CONTENT_CONDITION_SCHEMA = {
|
|
2376
2581
|
type: "object",
|
|
2377
2582
|
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`.",
|
|
@@ -2420,9 +2625,14 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2420
2625
|
type: "object",
|
|
2421
2626
|
properties: {
|
|
2422
2627
|
value: { type: "string", description: "Camel-case declared Value name." },
|
|
2423
|
-
type: { type: "string", enum: ["number"] },
|
|
2628
|
+
type: { type: "string", enum: ["number", "string"] },
|
|
2424
2629
|
finite: { type: "boolean" },
|
|
2425
2630
|
greaterThan: { type: "number" },
|
|
2631
|
+
integer: { type: "boolean" },
|
|
2632
|
+
allowedValues: {
|
|
2633
|
+
...STRING_ARRAY,
|
|
2634
|
+
description: "Accepted literals for a String Value constraint."
|
|
2635
|
+
},
|
|
2426
2636
|
suggestion: { type: "string" }
|
|
2427
2637
|
},
|
|
2428
2638
|
required: ["value", "type", "suggestion"]
|
|
@@ -2463,6 +2673,45 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2463
2673
|
required: ["whenPresent", "require", "suggestion"]
|
|
2464
2674
|
}
|
|
2465
2675
|
},
|
|
2676
|
+
requiredActions: {
|
|
2677
|
+
type: "array",
|
|
2678
|
+
description: "Action bindings required on a particular element in one configured Value state.",
|
|
2679
|
+
items: {
|
|
2680
|
+
type: "object",
|
|
2681
|
+
properties: {
|
|
2682
|
+
target: { type: "string", description: 'Target name; "" means the scope element.' },
|
|
2683
|
+
action: { type: "string", description: "Public action method that must be wired." },
|
|
2684
|
+
eventTypes: {
|
|
2685
|
+
...STRING_ARRAY,
|
|
2686
|
+
description: "Event types that make the binding usable for this contract."
|
|
2687
|
+
},
|
|
2688
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2689
|
+
suggestion: { type: "string" }
|
|
2690
|
+
},
|
|
2691
|
+
required: ["target", "action", "eventTypes", "suggestion"]
|
|
2692
|
+
}
|
|
2693
|
+
},
|
|
2694
|
+
actionCompletion: {
|
|
2695
|
+
type: "array",
|
|
2696
|
+
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.",
|
|
2697
|
+
items: {
|
|
2698
|
+
type: "object",
|
|
2699
|
+
properties: {
|
|
2700
|
+
opens: { type: "string", description: "Action that opens the state." },
|
|
2701
|
+
whenTriggeredBy: {
|
|
2702
|
+
...STRING_ARRAY,
|
|
2703
|
+
description: "Event types for that action the controller cannot complete itself."
|
|
2704
|
+
},
|
|
2705
|
+
closedBy: { ...STRING_ARRAY, description: "Any one of these closes it again." },
|
|
2706
|
+
escapeValue: {
|
|
2707
|
+
type: "string",
|
|
2708
|
+
description: "Value whose non-zero number supplies an automatic exit instead."
|
|
2709
|
+
},
|
|
2710
|
+
suggestion: { type: "string" }
|
|
2711
|
+
},
|
|
2712
|
+
required: ["opens", "whenTriggeredBy", "closedBy", "suggestion"]
|
|
2713
|
+
}
|
|
2714
|
+
},
|
|
2466
2715
|
a11y: {
|
|
2467
2716
|
type: "array",
|
|
2468
2717
|
description: "Author-supplied ARIA the controller relies on but never sets itself.",
|
|
@@ -2484,16 +2733,7 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2484
2733
|
required: ["attrs"]
|
|
2485
2734
|
}
|
|
2486
2735
|
},
|
|
2487
|
-
when:
|
|
2488
|
-
type: "object",
|
|
2489
|
-
description: "Own-value condition arming the requirement; absent means unconditional. Outside the named configuration the ARIA is not optional but wrong.",
|
|
2490
|
-
properties: {
|
|
2491
|
-
value: { type: "string" },
|
|
2492
|
-
equals: STRING_ARRAY,
|
|
2493
|
-
default: { type: "string" }
|
|
2494
|
-
},
|
|
2495
|
-
required: ["value", "equals", "default"]
|
|
2496
|
-
},
|
|
2736
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2497
2737
|
whenContains: CONTENT_CONDITION_SCHEMA,
|
|
2498
2738
|
whenElement: ELEMENT_CONDITION_SCHEMA,
|
|
2499
2739
|
whenDocument: DOCUMENT_CONDITION_SCHEMA,
|
|
@@ -2577,16 +2817,7 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2577
2817
|
description: "Check the scope element when the target is absent."
|
|
2578
2818
|
},
|
|
2579
2819
|
coController: { type: "string", description: "Companion controller identifier." },
|
|
2580
|
-
when:
|
|
2581
|
-
type: "object",
|
|
2582
|
-
description: "Host-value condition arming the rule; absent means unconditional.",
|
|
2583
|
-
properties: {
|
|
2584
|
-
value: { type: "string" },
|
|
2585
|
-
equals: STRING_ARRAY,
|
|
2586
|
-
default: { type: "string" }
|
|
2587
|
-
},
|
|
2588
|
-
required: ["value", "equals", "default"]
|
|
2589
|
-
},
|
|
2820
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2590
2821
|
require: {
|
|
2591
2822
|
type: "object",
|
|
2592
2823
|
description: "Companion-value requirement (first allowed value is canonical).",
|
|
@@ -2650,16 +2881,7 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2650
2881
|
values: { ...STRING_ARRAY, description: "Restrict the attribute to these values." },
|
|
2651
2882
|
min: { type: "integer", description: "Smallest permitted count (inclusive)." },
|
|
2652
2883
|
max: { type: "integer", description: "Largest permitted count (inclusive)." },
|
|
2653
|
-
when:
|
|
2654
|
-
type: "object",
|
|
2655
|
-
description: "Own-value condition arming the bound; absent means unconditional.",
|
|
2656
|
-
properties: {
|
|
2657
|
-
value: { type: "string" },
|
|
2658
|
-
equals: STRING_ARRAY,
|
|
2659
|
-
default: { type: "string" }
|
|
2660
|
-
},
|
|
2661
|
-
required: ["value", "equals", "default"]
|
|
2662
|
-
},
|
|
2884
|
+
when: VALUE_CONDITION_SCHEMA,
|
|
2663
2885
|
suggestion: { type: "string" }
|
|
2664
2886
|
},
|
|
2665
2887
|
required: ["within", "target", "suggestion"]
|
|
@@ -2691,6 +2913,8 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
2691
2913
|
"events",
|
|
2692
2914
|
"requiredTargets",
|
|
2693
2915
|
"conditionalTargets",
|
|
2916
|
+
"requiredActions",
|
|
2917
|
+
"actionCompletion",
|
|
2694
2918
|
"a11y",
|
|
2695
2919
|
"keyboard",
|
|
2696
2920
|
"hosts",
|