stimeo-ui 0.15.0 → 0.16.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/README.md +120 -0
- package/dist/cable/index.js +63 -15
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +30 -3
- package/dist/controllers/accordion_controller.js +98 -9
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +8 -7
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +3 -2
- package/dist/controllers/announcer_controller.js +8 -1
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +43 -2
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +1 -1
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +9 -8
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +4 -4
- package/dist/controllers/bulk_select_controller.js +7 -6
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +85 -23
- package/dist/controllers/calendar_controller.js +323 -123
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +67 -22
- package/dist/controllers/carousel_controller.js +263 -38
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +1 -1
- package/dist/controllers/character_counter_controller.js +40 -2
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.js +54 -10
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +28 -2
- package/dist/controllers/clipboard_controller.js +63 -5
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +25 -1
- package/dist/controllers/collapsible_controller.js +99 -14
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +29 -8
- package/dist/controllers/color_picker_controller.js +80 -34
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +10 -1
- package/dist/controllers/combobox_controller.js +106 -16
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +3 -3
- package/dist/controllers/command_palette_controller.js +35 -3
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.js +56 -11
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js +3 -0
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +6 -0
- package/dist/controllers/context_menu_controller.js +32 -12
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +30 -1
- package/dist/controllers/countdown_controller.js +129 -26
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +74 -15
- package/dist/controllers/currency_input_controller.js +205 -58
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +63 -18
- package/dist/controllers/data_grid_controller.js +195 -29
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +34 -6
- package/dist/controllers/date_range_picker_controller.js +139 -27
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +10 -3
- package/dist/controllers/dialog_controller.js +35 -8
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.js +22 -4
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +2 -2
- package/dist/controllers/dirty_form_controller.js +14 -1
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +1 -0
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +18 -10
- package/dist/controllers/drawer_controller.js +54 -19
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +9 -3
- package/dist/controllers/dropdown_controller.js +36 -9
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +34 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.js +144 -51
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +10 -4
- package/dist/controllers/filter_controller.js +20 -6
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +26 -6
- package/dist/controllers/flash_controller.js +305 -46
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.js +1 -0
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +7 -5
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +19 -13
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.js +16 -2
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +19 -2
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +10 -2
- package/dist/controllers/hover_card_controller.js +40 -14
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +16 -3
- package/dist/controllers/idle_controller.js +90 -5
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +5 -2
- package/dist/controllers/input_mask_controller.js +65 -9
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +3 -0
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +11 -2
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +50 -8
- package/dist/controllers/listbox_controller.js +203 -45
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.js +0 -2
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +9 -3
- package/dist/controllers/menu_controller.js +45 -16
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +11 -0
- package/dist/controllers/menubar_controller.js +58 -24
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +9 -5
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +18 -3
- package/dist/controllers/multi_select_controller.js +278 -104
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +11 -0
- package/dist/controllers/navigation_menu_controller.js +48 -15
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.js +37 -8
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.js +9 -1
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +36 -8
- package/dist/controllers/number_input_controller.js +124 -21
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +14 -1
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +22 -7
- package/dist/controllers/otp_controller.js +198 -55
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
- package/dist/controllers/overflow_indicator_controller.js +84 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +26 -6
- package/dist/controllers/overflow_menu_controller.js +112 -41
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +19 -11
- package/dist/controllers/pagination_controller.js +74 -28
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +15 -1
- package/dist/controllers/password_reveal_controller.js +59 -2
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +30 -8
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +131 -52
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +9 -3
- package/dist/controllers/popover_controller.js +45 -11
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -1
- package/dist/controllers/portal_controller.js +6 -2
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.js +16 -1
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js +8 -4
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +6 -4
- package/dist/controllers/radio_group_controller.js +42 -17
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +49 -1
- package/dist/controllers/range_slider_controller.js +88 -42
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +14 -3
- package/dist/controllers/rating_controller.js +39 -15
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +24 -2
- package/dist/controllers/read_more_controller.js +69 -2
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +16 -2
- package/dist/controllers/resizable_controller.js +82 -22
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +5 -1
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +0 -1
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +3 -2
- package/dist/controllers/scrollspy_controller.js +6 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +41 -14
- package/dist/controllers/separator_controller.js +66 -37
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +20 -3
- package/dist/controllers/sidebar_controller.js +77 -18
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +6 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +45 -7
- package/dist/controllers/slider_controller.js +82 -47
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.js +11 -1
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +17 -2
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +10 -2
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +19 -17
- package/dist/controllers/step_indicator_controller.js +18 -17
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +34 -9
- package/dist/controllers/stepper_controller.js +101 -19
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
- package/dist/controllers/stick_to_bottom_controller.js +75 -5
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +3 -2
- package/dist/controllers/submit_once_controller.js +16 -3
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +29 -7
- package/dist/controllers/switch_controller.js +101 -10
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +12 -0
- package/dist/controllers/tabs_controller.js +21 -2
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +15 -3
- package/dist/controllers/tags_input_controller.js +209 -59
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +20 -3
- package/dist/controllers/theme_controller.js +64 -14
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +23 -8
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +52 -15
- package/dist/controllers/toast_controller.js +337 -54
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +49 -7
- package/dist/controllers/toggle_group_controller.js +159 -23
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +32 -0
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +8 -0
- package/dist/controllers/tooltip_controller.js +39 -13
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js +4 -0
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +39 -8
- package/dist/controllers/tree_view_controller.js +169 -16
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4588 -1535
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +72 -6
- package/dist/inspector/cli.js +262 -51
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +309 -51
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +40 -40
- package/dist/inspector/manifest.json +525 -59
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
package/dist/inspector/cli.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ interface ControllerManifest {
|
|
|
38
38
|
readonly valueSyntaxConstraints?: readonly StringSyntaxConstraint[];
|
|
39
39
|
/** Statically decidable relationships between literal Stimulus Values. */
|
|
40
40
|
readonly valueRelations: readonly ValueRelation[];
|
|
41
|
+
/** What each action reads out of `event.params`. */
|
|
42
|
+
readonly actionParams: readonly ActionParamRule[];
|
|
41
43
|
/**
|
|
42
44
|
* Public action method names declared via `static actions`, wired by
|
|
43
45
|
* consumers as `data-action="…-><identifier>#<action>"`.
|
|
@@ -55,6 +57,11 @@ interface ControllerManifest {
|
|
|
55
57
|
* {@link ConditionalTargetRule}.
|
|
56
58
|
*/
|
|
57
59
|
readonly conditionalTargets: readonly ConditionalTargetRule[];
|
|
60
|
+
/**
|
|
61
|
+
* `<template>` targets whose content must be exactly one row. See
|
|
62
|
+
* {@link TemplateRootRule}.
|
|
63
|
+
*/
|
|
64
|
+
readonly templateRoots: readonly TemplateRootRule[];
|
|
58
65
|
/**
|
|
59
66
|
* Actions required on a particular element in one Value configuration. See
|
|
60
67
|
* {@link RequiredActionRule}.
|
|
@@ -592,6 +599,33 @@ interface ForbiddenAriaRule {
|
|
|
592
599
|
/** Human-readable fix suggestion shown by the CLI (stage 4). */
|
|
593
600
|
readonly suggestion: string;
|
|
594
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* One `event.params` entry an action reads, written as the author writes it.
|
|
604
|
+
*
|
|
605
|
+
* Stimulus builds `event.params` from `data-<identifier>-<param>-param` attributes
|
|
606
|
+
* on the element carrying the `data-action`. Nothing about that spelling is
|
|
607
|
+
* reflected, so a name that never arrives is indistinguishable at runtime from one
|
|
608
|
+
* the author never meant to pass: the action runs and returns, and the control is
|
|
609
|
+
* dead with no diagnostic anywhere.
|
|
610
|
+
*
|
|
611
|
+
* `required` is reserved for an action that has **no other way** to reach the value.
|
|
612
|
+
* Where the reader falls back to `event.detail` or to an attribute, the param is
|
|
613
|
+
* genuinely optional and a page driving the action that way is correct.
|
|
614
|
+
*/
|
|
615
|
+
interface ActionParamRule {
|
|
616
|
+
/** The action method the param belongs to; must appear in `actions`. */
|
|
617
|
+
readonly action: string;
|
|
618
|
+
/** The param name, camelCase, as `event.params` reads it. */
|
|
619
|
+
readonly param: string;
|
|
620
|
+
/** Whether the action can reach the value no other way. */
|
|
621
|
+
readonly required?: boolean;
|
|
622
|
+
/** The literal values the reader accepts, when it accepts a fixed set. */
|
|
623
|
+
readonly allowedValues?: readonly string[];
|
|
624
|
+
/** Whether the reader takes a whole number and returns on anything else. */
|
|
625
|
+
readonly integer?: boolean;
|
|
626
|
+
/** Human-readable fix suggestion shown by the CLI. */
|
|
627
|
+
readonly suggestion: string;
|
|
628
|
+
}
|
|
595
629
|
/**
|
|
596
630
|
* The bundled manifest. `schemaVersion` tracks the manifest *format*;
|
|
597
631
|
* `packageVersion` tracks the `stimeo-ui` release it was generated from so a
|
|
@@ -603,6 +637,24 @@ interface Manifest {
|
|
|
603
637
|
/** Keyed by controller identifier, e.g. `stimeo--menu`. */
|
|
604
638
|
readonly controllers: Readonly<Record<string, ControllerManifest>>;
|
|
605
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* The shape a `<template>` target's content has to have: **exactly one element**,
|
|
642
|
+
* which is the row the controller clones.
|
|
643
|
+
*
|
|
644
|
+
* A row is cloned on its own so that the node appended is the node a later
|
|
645
|
+
* removal takes back. Content the row does not cover — a second element, a
|
|
646
|
+
* wrapper around it — is therefore never rendered, and the widget silently adds
|
|
647
|
+
* nothing. A controller that reports it names it on the console once per
|
|
648
|
+
* connection; this rule says so before the page is served.
|
|
649
|
+
*/
|
|
650
|
+
interface TemplateRootRule {
|
|
651
|
+
/** Target name of the `<template>`. */
|
|
652
|
+
readonly template: string;
|
|
653
|
+
/** Target name its one element must carry, when the row is a declared part. */
|
|
654
|
+
readonly rootTarget?: string;
|
|
655
|
+
/** What to write instead. */
|
|
656
|
+
readonly suggestion: string;
|
|
657
|
+
}
|
|
606
658
|
/**
|
|
607
659
|
* A target requirement that only exists once another *optional* target is
|
|
608
660
|
* present.
|
|
@@ -625,8 +677,8 @@ interface ConditionalTargetRule {
|
|
|
625
677
|
/** Targets that become required once {@link whenPresent} appears. */
|
|
626
678
|
readonly require: readonly string[];
|
|
627
679
|
/**
|
|
628
|
-
* Require each {@link require} entry to sit
|
|
629
|
-
* element rather than anywhere in the scope.
|
|
680
|
+
* Require each {@link ConditionalTargetRule.require | require} entry to sit
|
|
681
|
+
* **inside** the `whenPresent` element rather than anywhere in the scope.
|
|
630
682
|
*
|
|
631
683
|
* A `<template>` is cloned at runtime, so a part declared outside it never
|
|
632
684
|
* reaches the clone. Without this the presence check passes on markup the
|
|
@@ -634,9 +686,9 @@ interface ConditionalTargetRule {
|
|
|
634
686
|
*/
|
|
635
687
|
readonly requireInside?: boolean;
|
|
636
688
|
/**
|
|
637
|
-
* Require each {@link require} entry to share its
|
|
638
|
-
* the closest ancestor matching one of these selectors —
|
|
639
|
-
* `whenPresent` element.
|
|
689
|
+
* Require each {@link ConditionalTargetRule.require | require} entry to share its
|
|
690
|
+
* nearest enclosing host — the closest ancestor matching one of these selectors —
|
|
691
|
+
* with the `whenPresent` element.
|
|
640
692
|
*
|
|
641
693
|
* Neither presence nor {@link requireInside} can express a pair that must sit
|
|
642
694
|
* in the *same* container without being nested in one another. A controller
|
|
@@ -650,6 +702,20 @@ interface ConditionalTargetRule {
|
|
|
650
702
|
* generated — reporting either would be a guess, and the check stays quiet.
|
|
651
703
|
*/
|
|
652
704
|
readonly requireSameHost?: readonly HostSelector[];
|
|
705
|
+
/**
|
|
706
|
+
* Require each {@link ConditionalTargetRule.require | require} entry to sit
|
|
707
|
+
* inside the same element carrying this controller's target of this name as the
|
|
708
|
+
* {@link whenPresent} entry does.
|
|
709
|
+
*
|
|
710
|
+
* Where {@link requireSameHost} describes the host by tag, this names it: the host
|
|
711
|
+
* is a declared target of the same controller, so a pair resolved per trigger can
|
|
712
|
+
* say so directly. Halves split across two of those hosts leave both incomplete,
|
|
713
|
+
* and neither swaps.
|
|
714
|
+
*
|
|
715
|
+
* A scope whose hosts are all generated leaves the question undecidable, so nothing
|
|
716
|
+
* is reported there.
|
|
717
|
+
*/
|
|
718
|
+
readonly requireSameTargetHost?: string;
|
|
653
719
|
/**
|
|
654
720
|
* What to call the shared host in the diagnostic (e.g. `submit control`).
|
|
655
721
|
* The selectors' tag names read as a disjunction of element names, which
|
|
@@ -806,7 +872,7 @@ type DiagnosticSeverity = "error" | "warning";
|
|
|
806
872
|
* for testing, machine-readable output, and `data-stimeo-ignore` suppression
|
|
807
873
|
* lists (which is why the set also exists as a runtime array).
|
|
808
874
|
*/
|
|
809
|
-
declare const DIAGNOSTIC_CODES: readonly ["unknown-controller", "unknown-target", "unknown-value", "invalid-value", "unknown-action-controller", "unknown-action-method", "orphan-target", "missing-required-target", "missing-conditional-target", "missing-required-action", "missing-action-completion", "missing-action-event", "missing-aria", "invalid-aria-value", "keyboard-inaccessible", "invalid-host", "unresolved-idref", "managed-aria", "composition-mismatch", "missing-companion", "undeclared-target", "cardinality-violation", "forbidden-aria", "missing-announcer", "unknown-ignore-code"];
|
|
875
|
+
declare const DIAGNOSTIC_CODES: readonly ["unknown-controller", "unknown-target", "unknown-value", "invalid-value", "unknown-action-controller", "unknown-action-method", "unknown-action-event", "orphan-target", "missing-required-target", "missing-conditional-target", "invalid-template-root", "missing-required-action", "missing-action-completion", "missing-action-event", "missing-aria", "invalid-aria-value", "keyboard-inaccessible", "invalid-host", "unresolved-idref", "managed-aria", "composition-mismatch", "missing-companion", "undeclared-target", "cardinality-violation", "forbidden-aria", "missing-announcer", "missing-action-param", "invalid-action-param", "confusable-action-param", "unknown-ignore-code"];
|
|
810
876
|
/** See {@link DIAGNOSTIC_CODES}. */
|
|
811
877
|
type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[number];
|
|
812
878
|
/**
|
package/dist/inspector/cli.js
CHANGED
|
@@ -980,12 +980,24 @@ var NAMESPACE_PREFIX = "stimeo--";
|
|
|
980
980
|
function dasherize(value) {
|
|
981
981
|
return value.replace(/([A-Z])/g, (_match, char) => `-${char.toLowerCase()}`);
|
|
982
982
|
}
|
|
983
|
-
function
|
|
983
|
+
function attributeTokens(value) {
|
|
984
|
+
const tokens = [];
|
|
984
985
|
const seen = /* @__PURE__ */ new Set();
|
|
985
|
-
|
|
986
|
-
|
|
986
|
+
const pattern = /\S+/g;
|
|
987
|
+
for (let match = pattern.exec(value); match; match = pattern.exec(value)) {
|
|
988
|
+
if (seen.has(match[0])) continue;
|
|
989
|
+
seen.add(match[0]);
|
|
990
|
+
tokens.push({ name: match[0], start: match.index });
|
|
987
991
|
}
|
|
988
|
-
return
|
|
992
|
+
return tokens;
|
|
993
|
+
}
|
|
994
|
+
function controllerIdentifiers(dataControllerValue) {
|
|
995
|
+
return controllerIdentifierTokens(dataControllerValue).map((token) => token.name);
|
|
996
|
+
}
|
|
997
|
+
function controllerIdentifierTokens(dataControllerValue) {
|
|
998
|
+
return attributeTokens(dataControllerValue).filter(
|
|
999
|
+
(token) => token.name.startsWith(NAMESPACE_PREFIX)
|
|
1000
|
+
);
|
|
989
1001
|
}
|
|
990
1002
|
function parseTargetAttr(attrName) {
|
|
991
1003
|
const match = /^data-(stimeo--[a-z0-9][a-z0-9-]*)-target$/.exec(attrName);
|
|
@@ -1027,7 +1039,7 @@ function defaultEventFor(host) {
|
|
|
1027
1039
|
}
|
|
1028
1040
|
function actionDescriptors(dataActionValue, host) {
|
|
1029
1041
|
const descriptors = [];
|
|
1030
|
-
for (const descriptor of dataActionValue
|
|
1042
|
+
for (const { name: descriptor, start } of attributeTokens(dataActionValue)) {
|
|
1031
1043
|
const hash = descriptor.indexOf("#");
|
|
1032
1044
|
if (hash === -1) continue;
|
|
1033
1045
|
let lhs = descriptor.slice(0, hash);
|
|
@@ -1043,7 +1055,14 @@ function actionDescriptors(dataActionValue, host) {
|
|
|
1043
1055
|
}
|
|
1044
1056
|
if (!lhs.startsWith(NAMESPACE_PREFIX)) continue;
|
|
1045
1057
|
const method = /^[a-zA-Z_$][\w$]*/.exec(descriptor.slice(hash + 1))?.[0] ?? "";
|
|
1046
|
-
descriptors.push({
|
|
1058
|
+
descriptors.push({
|
|
1059
|
+
identifier: lhs,
|
|
1060
|
+
identifierStart: start + (arrow === -1 ? 0 : arrow + 2),
|
|
1061
|
+
method,
|
|
1062
|
+
methodStart: start + hash + 1,
|
|
1063
|
+
eventType,
|
|
1064
|
+
eventStart: start
|
|
1065
|
+
});
|
|
1047
1066
|
}
|
|
1048
1067
|
return descriptors;
|
|
1049
1068
|
}
|
|
@@ -1059,9 +1078,11 @@ var DIAGNOSTIC_CODES = [
|
|
|
1059
1078
|
"invalid-value",
|
|
1060
1079
|
"unknown-action-controller",
|
|
1061
1080
|
"unknown-action-method",
|
|
1081
|
+
"unknown-action-event",
|
|
1062
1082
|
"orphan-target",
|
|
1063
1083
|
"missing-required-target",
|
|
1064
1084
|
"missing-conditional-target",
|
|
1085
|
+
"invalid-template-root",
|
|
1065
1086
|
"missing-required-action",
|
|
1066
1087
|
"missing-action-completion",
|
|
1067
1088
|
"missing-action-event",
|
|
@@ -1077,11 +1098,14 @@ var DIAGNOSTIC_CODES = [
|
|
|
1077
1098
|
"cardinality-violation",
|
|
1078
1099
|
"forbidden-aria",
|
|
1079
1100
|
"missing-announcer",
|
|
1101
|
+
"missing-action-param",
|
|
1102
|
+
"invalid-action-param",
|
|
1103
|
+
"confusable-action-param",
|
|
1080
1104
|
"unknown-ignore-code"
|
|
1081
1105
|
];
|
|
1082
1106
|
|
|
1083
1107
|
// src/inspector/check.ts
|
|
1084
|
-
function
|
|
1108
|
+
function isDescendantOf(node, ancestor) {
|
|
1085
1109
|
for (let current = node.parent; current; current = current.parent) {
|
|
1086
1110
|
if (current === ancestor) return true;
|
|
1087
1111
|
}
|
|
@@ -1174,11 +1198,13 @@ function checkSource(source, manifest) {
|
|
|
1174
1198
|
const unnamedTargets = [];
|
|
1175
1199
|
const targetsUnreadable = (container, scope, identifier) => {
|
|
1176
1200
|
for (const node of unresolved) {
|
|
1177
|
-
if (
|
|
1201
|
+
if (isSelfOrDescendantOf(node, container) && findOwner(node, identifier) === scope)
|
|
1202
|
+
return true;
|
|
1178
1203
|
}
|
|
1179
1204
|
for (const { node, identifier: hidden } of unnamedTargets) {
|
|
1180
1205
|
if (hidden !== identifier) continue;
|
|
1181
|
-
if (
|
|
1206
|
+
if (isSelfOrDescendantOf(node, container) && findOwner(node, identifier) === scope)
|
|
1207
|
+
return true;
|
|
1182
1208
|
}
|
|
1183
1209
|
return false;
|
|
1184
1210
|
};
|
|
@@ -1237,7 +1263,7 @@ function checkSource(source, manifest) {
|
|
|
1237
1263
|
const nodes = targetNodes.get(scope)?.get(identifier)?.get(condition.target) ?? [];
|
|
1238
1264
|
const container = condition.within === "scope" ? scope : element;
|
|
1239
1265
|
let held = 0;
|
|
1240
|
-
for (const node of nodes) if (
|
|
1266
|
+
for (const node of nodes) if (isSelfOrDescendantOf(node, container)) held += 1;
|
|
1241
1267
|
if (condition.min !== void 0 && held < condition.min) return false;
|
|
1242
1268
|
if (condition.max !== void 0 && held > condition.max) return false;
|
|
1243
1269
|
return true;
|
|
@@ -1274,9 +1300,9 @@ function checkSource(source, manifest) {
|
|
|
1274
1300
|
const authoredValueIdentifiers = /* @__PURE__ */ new Set();
|
|
1275
1301
|
for (const attr of node.attrs) {
|
|
1276
1302
|
if (attr.name === IGNORE_ATTR) {
|
|
1277
|
-
for (const token of attr.value
|
|
1278
|
-
if (!DIAGNOSTIC_CODES.includes(token)) {
|
|
1279
|
-
const best = nearestName(token, DIAGNOSTIC_CODES);
|
|
1303
|
+
for (const token of attributeTokens(attr.value)) {
|
|
1304
|
+
if (!DIAGNOSTIC_CODES.includes(token.name)) {
|
|
1305
|
+
const best = nearestName(token.name, DIAGNOSTIC_CODES);
|
|
1280
1306
|
push(
|
|
1281
1307
|
diagnostics,
|
|
1282
1308
|
"unknown-ignore-code",
|
|
@@ -1291,7 +1317,7 @@ function checkSource(source, manifest) {
|
|
|
1291
1317
|
continue;
|
|
1292
1318
|
}
|
|
1293
1319
|
if (attr.name === FRAGMENT_ATTR) {
|
|
1294
|
-
const identifiers = attr.value
|
|
1320
|
+
const identifiers = attributeTokens(attr.value);
|
|
1295
1321
|
if (identifiers.length === 0) {
|
|
1296
1322
|
report(
|
|
1297
1323
|
node,
|
|
@@ -1302,13 +1328,13 @@ function checkSource(source, manifest) {
|
|
|
1302
1328
|
);
|
|
1303
1329
|
}
|
|
1304
1330
|
for (const identifier of identifiers) {
|
|
1305
|
-
if (!(identifier in known)) {
|
|
1306
|
-
const best = nearestName(identifier, knownIdentifiers);
|
|
1331
|
+
if (!(identifier.name in known)) {
|
|
1332
|
+
const best = nearestName(identifier.name, knownIdentifiers);
|
|
1307
1333
|
report(
|
|
1308
1334
|
node,
|
|
1309
1335
|
"unknown-controller",
|
|
1310
1336
|
"error",
|
|
1311
|
-
`Fragment declaration references unknown Stimeo controller "${identifier}".`,
|
|
1337
|
+
`Fragment declaration references unknown Stimeo controller "${identifier.name}".`,
|
|
1312
1338
|
attr,
|
|
1313
1339
|
asDidYouMean(best),
|
|
1314
1340
|
tokenFix(attr, identifier, best)
|
|
@@ -1318,7 +1344,8 @@ function checkSource(source, manifest) {
|
|
|
1318
1344
|
continue;
|
|
1319
1345
|
}
|
|
1320
1346
|
if (attr.name === "data-controller") {
|
|
1321
|
-
for (const
|
|
1347
|
+
for (const token of controllerIdentifierTokens(attr.value)) {
|
|
1348
|
+
const identifier = token.name;
|
|
1322
1349
|
if (identifier in known) {
|
|
1323
1350
|
scopes.push({ node, identifier });
|
|
1324
1351
|
} else {
|
|
@@ -1330,7 +1357,7 @@ function checkSource(source, manifest) {
|
|
|
1330
1357
|
`Unknown Stimeo controller "${identifier}".`,
|
|
1331
1358
|
attr,
|
|
1332
1359
|
asDidYouMean(best),
|
|
1333
|
-
tokenFix(attr,
|
|
1360
|
+
tokenFix(attr, token, best)
|
|
1334
1361
|
);
|
|
1335
1362
|
}
|
|
1336
1363
|
}
|
|
@@ -1350,24 +1377,27 @@ function checkSource(source, manifest) {
|
|
|
1350
1377
|
);
|
|
1351
1378
|
continue;
|
|
1352
1379
|
}
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1380
|
+
const declared = isDynamicValue(attr) ? [] : attributeTokens(attr.value);
|
|
1381
|
+
for (const token of declared) {
|
|
1382
|
+
if (controller.targets.includes(token.name)) continue;
|
|
1383
|
+
const best = nearestName(token.name, controller.targets);
|
|
1356
1384
|
report(
|
|
1357
1385
|
node,
|
|
1358
1386
|
"unknown-target",
|
|
1359
1387
|
"error",
|
|
1360
|
-
`Unknown target "${
|
|
1388
|
+
`Unknown target "${token.name}" for "${targetIdentifier}". Known targets: ${list(controller.targets)}.`,
|
|
1361
1389
|
attr,
|
|
1362
1390
|
asDidYouMean(best),
|
|
1363
|
-
tokenFix(attr,
|
|
1391
|
+
tokenFix(attr, token, best)
|
|
1364
1392
|
);
|
|
1365
1393
|
}
|
|
1366
1394
|
const owner = findOwner(node, targetIdentifier);
|
|
1367
1395
|
if (owner) {
|
|
1368
|
-
if (
|
|
1369
|
-
|
|
1370
|
-
|
|
1396
|
+
if (declared.length > 0) {
|
|
1397
|
+
for (const token of declared) {
|
|
1398
|
+
recordPresence(owner, targetIdentifier, token.name);
|
|
1399
|
+
recordTargetNode(owner, targetIdentifier, token.name, node);
|
|
1400
|
+
}
|
|
1371
1401
|
} else if (isDynamicValue(attr)) {
|
|
1372
1402
|
unnamedTargets.push({ node, identifier: targetIdentifier });
|
|
1373
1403
|
}
|
|
@@ -1431,7 +1461,15 @@ function checkSource(source, manifest) {
|
|
|
1431
1461
|
tag: node.tag,
|
|
1432
1462
|
inputType: node.attrs.find((a) => a.name === "type")?.value
|
|
1433
1463
|
};
|
|
1434
|
-
|
|
1464
|
+
const paramsChecked = /* @__PURE__ */ new Set();
|
|
1465
|
+
for (const {
|
|
1466
|
+
identifier,
|
|
1467
|
+
identifierStart,
|
|
1468
|
+
method,
|
|
1469
|
+
methodStart,
|
|
1470
|
+
eventType,
|
|
1471
|
+
eventStart
|
|
1472
|
+
} of actionDescriptors(attr.value, host)) {
|
|
1435
1473
|
const controller = known[identifier];
|
|
1436
1474
|
if (controller && method.length > 0) {
|
|
1437
1475
|
const owner = findOwner(node, identifier);
|
|
@@ -1456,7 +1494,7 @@ function checkSource(source, manifest) {
|
|
|
1456
1494
|
`Action references unknown Stimeo controller "${identifier}".`,
|
|
1457
1495
|
attr,
|
|
1458
1496
|
asDidYouMean(best),
|
|
1459
|
-
tokenFix(attr, identifier, best)
|
|
1497
|
+
tokenFix(attr, { name: identifier, start: identifierStart }, best)
|
|
1460
1498
|
);
|
|
1461
1499
|
} else if (method.length > 0 && !controller.actions.includes(method)) {
|
|
1462
1500
|
const best = nearestName(method, controller.actions);
|
|
@@ -1467,12 +1505,124 @@ function checkSource(source, manifest) {
|
|
|
1467
1505
|
`Unknown action "${method}" for "${identifier}". Known actions: ${list(controller.actions)}.`,
|
|
1468
1506
|
attr,
|
|
1469
1507
|
asDidYouMean(best),
|
|
1470
|
-
tokenFix(attr, method, best)
|
|
1508
|
+
tokenFix(attr, { name: method, start: methodStart }, best)
|
|
1471
1509
|
);
|
|
1472
1510
|
}
|
|
1511
|
+
if (controller && method.length > 0 && !paramsChecked.has(`${identifier}#${method}`)) {
|
|
1512
|
+
paramsChecked.add(`${identifier}#${method}`);
|
|
1513
|
+
checkActionParams(node, attr, identifier, method, controller);
|
|
1514
|
+
}
|
|
1515
|
+
if (!isDynamicValue(attr)) {
|
|
1516
|
+
const emitted = /^(stimeo--[a-z0-9-]+):(.+)$/.exec(eventType);
|
|
1517
|
+
const source2 = emitted ? known[emitted[1]] : void 0;
|
|
1518
|
+
const name = emitted?.[2];
|
|
1519
|
+
if (source2 && name !== void 0 && !source2.events.includes(name)) {
|
|
1520
|
+
const best = nearestName(name, source2.events);
|
|
1521
|
+
report(
|
|
1522
|
+
node,
|
|
1523
|
+
"unknown-action-event",
|
|
1524
|
+
"error",
|
|
1525
|
+
`Unknown event "${name}" for "${emitted?.[1]}". Known events: ${list(source2.events)}.`,
|
|
1526
|
+
attr,
|
|
1527
|
+
asDidYouMean(best),
|
|
1528
|
+
tokenFix(attr, { name, start: eventStart + eventType.length - name.length }, best)
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1473
1532
|
}
|
|
1474
1533
|
}
|
|
1475
1534
|
}
|
|
1535
|
+
function checkActionParams(element, action, identifier, method, controller) {
|
|
1536
|
+
const rules = controller.actionParams.filter((rule) => rule.action === method);
|
|
1537
|
+
if (rules.length === 0) return;
|
|
1538
|
+
const readable = !isDynamicValue(action) && element.origin === "markup" && !element.opaque;
|
|
1539
|
+
const expected = new Map(
|
|
1540
|
+
rules.map((rule) => [`data-${identifier}-${dasherize(rule.param)}-param`, rule])
|
|
1541
|
+
);
|
|
1542
|
+
for (const [attrName, rule] of expected) {
|
|
1543
|
+
const written = element.attrs.find((candidate) => candidate.name === attrName);
|
|
1544
|
+
if (!written) {
|
|
1545
|
+
if (!rule.required || !readable) continue;
|
|
1546
|
+
const confusable = confusableParamAttr(element, attrName, rule.param);
|
|
1547
|
+
report(
|
|
1548
|
+
element,
|
|
1549
|
+
"missing-action-param",
|
|
1550
|
+
"error",
|
|
1551
|
+
confusable ? `"${identifier}#${method}" reads its "${rule.param}" param, and "${confusable.name}" is not the attribute Stimulus builds it from.` : `"${identifier}#${method}" reads its "${rule.param}" param, which nothing on this element supplies.`,
|
|
1552
|
+
confusable ?? action,
|
|
1553
|
+
rule.suggestion
|
|
1554
|
+
);
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
if (isDynamicValue(written)) continue;
|
|
1558
|
+
const value = written.value;
|
|
1559
|
+
if (value.length === 0) {
|
|
1560
|
+
if (!rule.required || !readable) continue;
|
|
1561
|
+
report(
|
|
1562
|
+
element,
|
|
1563
|
+
"missing-action-param",
|
|
1564
|
+
"error",
|
|
1565
|
+
`"${identifier}#${method}" reads its "${rule.param}" param, and "${attrName}" carries no value.`,
|
|
1566
|
+
written,
|
|
1567
|
+
rule.suggestion
|
|
1568
|
+
);
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
if (rule.integer && !Number.isInteger(Number(value))) {
|
|
1572
|
+
report(
|
|
1573
|
+
element,
|
|
1574
|
+
"invalid-action-param",
|
|
1575
|
+
"error",
|
|
1576
|
+
`"${value}" is not an "${rule.param}" this action reads. It accepts a whole number.`,
|
|
1577
|
+
written,
|
|
1578
|
+
rule.suggestion
|
|
1579
|
+
);
|
|
1580
|
+
continue;
|
|
1581
|
+
}
|
|
1582
|
+
if (!rule.allowedValues || rule.allowedValues.includes(value)) continue;
|
|
1583
|
+
const best = nearestName(value, rule.allowedValues);
|
|
1584
|
+
report(
|
|
1585
|
+
element,
|
|
1586
|
+
"invalid-action-param",
|
|
1587
|
+
"error",
|
|
1588
|
+
`"${value}" is not a "${rule.param}" this action reads. It accepts ${list(rule.allowedValues)}.`,
|
|
1589
|
+
written,
|
|
1590
|
+
asDidYouMean(best) ?? rule.suggestion,
|
|
1591
|
+
best !== void 0 && written.valueStart !== void 0 ? {
|
|
1592
|
+
start: written.valueStart,
|
|
1593
|
+
end: written.valueStart + written.value.length,
|
|
1594
|
+
text: best,
|
|
1595
|
+
title: `Replace with "${best}"`
|
|
1596
|
+
} : void 0
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
if (!readable) return;
|
|
1600
|
+
for (const candidate of element.attrs) {
|
|
1601
|
+
if (!candidate.name.startsWith(`data-${identifier}-`)) continue;
|
|
1602
|
+
if (expected.has(candidate.name)) continue;
|
|
1603
|
+
if (parseTargetAttr(candidate.name) || candidate.name.endsWith("-value")) continue;
|
|
1604
|
+
const best = [...expected.keys()].find((name) => namesTheParam(candidate.name, name));
|
|
1605
|
+
if (best === void 0) continue;
|
|
1606
|
+
report(
|
|
1607
|
+
element,
|
|
1608
|
+
"confusable-action-param",
|
|
1609
|
+
"warning",
|
|
1610
|
+
`"${candidate.name}" is not an attribute Stimulus reads. Action params are named "<param>-param".`,
|
|
1611
|
+
candidate,
|
|
1612
|
+
`Did you mean ${best}?`
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
function namesTheParam(written, attrName) {
|
|
1617
|
+
if (written === attrName) return false;
|
|
1618
|
+
return `${written}-param` === attrName || nearestName(written, [attrName]) !== void 0;
|
|
1619
|
+
}
|
|
1620
|
+
function confusableParamAttr(element, attrName, param) {
|
|
1621
|
+
const bare = `data-${dasherize(param)}`;
|
|
1622
|
+
return element.attrs.find(
|
|
1623
|
+
(candidate) => candidate.name === bare || namesTheParam(candidate.name, attrName)
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1476
1626
|
for (const identifier of authoredValueIdentifiers) {
|
|
1477
1627
|
const controller = known[identifier];
|
|
1478
1628
|
if (!controller) continue;
|
|
@@ -1532,19 +1682,30 @@ function checkSource(source, manifest) {
|
|
|
1532
1682
|
const owners = nodesOf(rule.whenPresent);
|
|
1533
1683
|
const hosts = rule.requireSameHost;
|
|
1534
1684
|
const sharesHost = (target, selectors) => {
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1685
|
+
const hostOf = (el) => nearestHost(el, selectors);
|
|
1686
|
+
const ownerHosts = owners.map(hostOf).filter((host) => host !== null && host !== void 0);
|
|
1537
1687
|
const candidates = nodesOf(target);
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1688
|
+
return ownerHosts.every(
|
|
1689
|
+
(ownerHost) => candidates.some((el) => {
|
|
1690
|
+
const host = hostOf(el);
|
|
1691
|
+
if (host === null) return false;
|
|
1692
|
+
return host ? host === ownerHost : true;
|
|
1693
|
+
})
|
|
1694
|
+
);
|
|
1695
|
+
};
|
|
1696
|
+
const sharesTargetHost = (target, hostTarget) => {
|
|
1697
|
+
const hostNodes = nodesOf(hostTarget);
|
|
1698
|
+
const hostOf = (el) => hostNodes.find((host) => isDescendantOf(el, host)) ?? null;
|
|
1699
|
+
const ownerHosts = owners.map(hostOf).filter((host) => host !== null);
|
|
1700
|
+
const candidates = nodesOf(target);
|
|
1701
|
+
return ownerHosts.every((ownerHost) => candidates.some((el) => hostOf(el) === ownerHost));
|
|
1543
1702
|
};
|
|
1544
1703
|
const satisfied = (target) => {
|
|
1704
|
+
const hostTarget = rule.requireSameTargetHost;
|
|
1705
|
+
if (hostTarget) return sharesTargetHost(target, hostTarget);
|
|
1545
1706
|
if (hosts) return sharesHost(target, hosts);
|
|
1546
1707
|
if (rule.requireInside)
|
|
1547
|
-
return nodesOf(target).some((el) => owners.some((owner) =>
|
|
1708
|
+
return nodesOf(target).some((el) => owners.some((owner) => isDescendantOf(el, owner)));
|
|
1548
1709
|
return present.has(target);
|
|
1549
1710
|
};
|
|
1550
1711
|
const missing = rule.require.filter((target) => !satisfied(target));
|
|
@@ -1553,12 +1714,62 @@ function checkSource(source, manifest) {
|
|
|
1553
1714
|
node,
|
|
1554
1715
|
"missing-conditional-target",
|
|
1555
1716
|
complete ? "error" : "warning",
|
|
1556
|
-
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)}.`,
|
|
1717
|
+
rule.requireSameTargetHost ? `"${identifier}" requires ${list(missing)} in the same "${rule.requireSameTargetHost}" as its "${rule.whenPresent}".` : 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)}.`,
|
|
1557
1718
|
node,
|
|
1558
1719
|
complete ? rule.suggestion : UNRESOLVED_TARGET_HINT
|
|
1559
1720
|
);
|
|
1560
1721
|
}
|
|
1561
1722
|
}
|
|
1723
|
+
for (const { node, identifier } of scopes) {
|
|
1724
|
+
const controller = known[identifier];
|
|
1725
|
+
if (!controller) continue;
|
|
1726
|
+
const complete = scopeIsComplete(node, node, identifier);
|
|
1727
|
+
const nodesOf = (target) => targetNodes.get(node)?.get(identifier)?.get(target) ?? [];
|
|
1728
|
+
const hint = complete ? void 0 : UNRESOLVED_TARGET_HINT;
|
|
1729
|
+
for (const rule of controller.templateRoots) {
|
|
1730
|
+
for (const template of nodesOf(rule.template)) {
|
|
1731
|
+
if (template.origin === "markup" && template.tag !== "template") {
|
|
1732
|
+
report(
|
|
1733
|
+
node,
|
|
1734
|
+
"invalid-template-root",
|
|
1735
|
+
complete ? "error" : "warning",
|
|
1736
|
+
`"${identifier}" requires its "${rule.template}" to be a <template>, and it is a <${template.tag}>.`,
|
|
1737
|
+
template,
|
|
1738
|
+
hint ?? rule.suggestion
|
|
1739
|
+
);
|
|
1740
|
+
continue;
|
|
1741
|
+
}
|
|
1742
|
+
if (template.children.some((child) => child.origin === "erb")) continue;
|
|
1743
|
+
const rows = template.children;
|
|
1744
|
+
const row = rows[0];
|
|
1745
|
+
if (rows.length === 0) continue;
|
|
1746
|
+
if (rows.length !== 1 || !row) {
|
|
1747
|
+
report(
|
|
1748
|
+
node,
|
|
1749
|
+
"invalid-template-root",
|
|
1750
|
+
complete ? "error" : "warning",
|
|
1751
|
+
`"${identifier}" requires its "${rule.template}" to hold exactly one element, and it holds ${rows.length}.`,
|
|
1752
|
+
template,
|
|
1753
|
+
hint ?? rule.suggestion
|
|
1754
|
+
);
|
|
1755
|
+
continue;
|
|
1756
|
+
}
|
|
1757
|
+
if (!rule.rootTarget) continue;
|
|
1758
|
+
if (unnamedTargets.some((entry) => entry.node === row && entry.identifier === identifier)) {
|
|
1759
|
+
continue;
|
|
1760
|
+
}
|
|
1761
|
+
if (nodesOf(rule.rootTarget).includes(row)) continue;
|
|
1762
|
+
report(
|
|
1763
|
+
node,
|
|
1764
|
+
"invalid-template-root",
|
|
1765
|
+
complete ? "error" : "warning",
|
|
1766
|
+
`"${identifier}" requires the one element in its "${rule.template}" to be its "${rule.rootTarget}" itself.`,
|
|
1767
|
+
row,
|
|
1768
|
+
hint ?? rule.suggestion
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1562
1773
|
for (const { node, identifier } of scopes) {
|
|
1563
1774
|
const controller = known[identifier];
|
|
1564
1775
|
if (!controller) continue;
|
|
@@ -1809,7 +2020,7 @@ function checkSource(source, manifest) {
|
|
|
1809
2020
|
if (rule.values && !rule.values.includes(value)) continue;
|
|
1810
2021
|
if (findOwner(el, identifier) !== node) continue;
|
|
1811
2022
|
const declaredAs = el.attrs.find((a) => a.name === targetAttrName);
|
|
1812
|
-
if (declaredAs && !isDynamicValue(declaredAs) && declaredAs.value.
|
|
2023
|
+
if (declaredAs && !isDynamicValue(declaredAs) && attributeTokens(declaredAs.value).some((token) => token.name === rule.target)) {
|
|
1813
2024
|
continue;
|
|
1814
2025
|
}
|
|
1815
2026
|
if (declaredAs && isDynamicValue(declaredAs)) continue;
|
|
@@ -1837,7 +2048,7 @@ function checkSource(source, manifest) {
|
|
|
1837
2048
|
for (const container of containers) {
|
|
1838
2049
|
const matched = [];
|
|
1839
2050
|
for (const el of counted) {
|
|
1840
|
-
if (!
|
|
2051
|
+
if (!isSelfOrDescendantOf(el, container)) continue;
|
|
1841
2052
|
if (rule.attr === void 0) {
|
|
1842
2053
|
matched.push({ el, at: el });
|
|
1843
2054
|
continue;
|
|
@@ -1893,14 +2104,14 @@ function checkSource(source, manifest) {
|
|
|
1893
2104
|
for (const el of anchored) {
|
|
1894
2105
|
for (const attr of el.attrs) {
|
|
1895
2106
|
if (!IDREF_ATTRS.has(attr.name) || isDynamicValue(attr)) continue;
|
|
1896
|
-
for (const token of attr.value
|
|
1897
|
-
if (!ids.has(token)) {
|
|
1898
|
-
const best = nearestName(token, [...ids]);
|
|
2107
|
+
for (const token of attributeTokens(attr.value)) {
|
|
2108
|
+
if (!ids.has(token.name)) {
|
|
2109
|
+
const best = nearestName(token.name, [...ids]);
|
|
1899
2110
|
report(
|
|
1900
2111
|
el,
|
|
1901
2112
|
"unresolved-idref",
|
|
1902
2113
|
"warning",
|
|
1903
|
-
`${attr.name} references id "${token}", but no element in this file declares it.`,
|
|
2114
|
+
`${attr.name} references id "${token.name}", but no element in this file declares it.`,
|
|
1904
2115
|
attr,
|
|
1905
2116
|
asDidYouMean(best),
|
|
1906
2117
|
tokenFix(attr, token, best)
|
|
@@ -2016,7 +2227,7 @@ function checkA11y(report, el, identifier, req, severity, isDynamicValue) {
|
|
|
2016
2227
|
);
|
|
2017
2228
|
}
|
|
2018
2229
|
}
|
|
2019
|
-
function
|
|
2230
|
+
function isSelfOrDescendantOf(node, container) {
|
|
2020
2231
|
let current = node;
|
|
2021
2232
|
while (current) {
|
|
2022
2233
|
if (current === container) return true;
|
|
@@ -2121,15 +2332,13 @@ function push(out, code, severity, message, at, suggestion, fix) {
|
|
|
2121
2332
|
});
|
|
2122
2333
|
}
|
|
2123
2334
|
function tokenFix(attr, token, replacement) {
|
|
2124
|
-
if (replacement === void 0 || attr.valueStart === void 0 || token.length === 0) {
|
|
2335
|
+
if (replacement === void 0 || attr.valueStart === void 0 || token.name.length === 0) {
|
|
2125
2336
|
return void 0;
|
|
2126
2337
|
}
|
|
2127
|
-
const
|
|
2128
|
-
if (index < 0) return void 0;
|
|
2129
|
-
const start = attr.valueStart + index;
|
|
2338
|
+
const start = attr.valueStart + token.start;
|
|
2130
2339
|
return {
|
|
2131
2340
|
start,
|
|
2132
|
-
end: start + token.length,
|
|
2341
|
+
end: start + token.name.length,
|
|
2133
2342
|
text: replacement,
|
|
2134
2343
|
title: `Replace with "${replacement}"`
|
|
2135
2344
|
};
|
|
@@ -2196,10 +2405,12 @@ var CONTROLLER_ARRAY_FIELDS = [
|
|
|
2196
2405
|
"values",
|
|
2197
2406
|
"valueConstraints",
|
|
2198
2407
|
"valueRelations",
|
|
2408
|
+
"actionParams",
|
|
2199
2409
|
"actions",
|
|
2200
2410
|
"events",
|
|
2201
2411
|
"requiredTargets",
|
|
2202
2412
|
"conditionalTargets",
|
|
2413
|
+
"templateRoots",
|
|
2203
2414
|
"requiredActions",
|
|
2204
2415
|
"actionCompletion",
|
|
2205
2416
|
"a11y",
|