herb 0.10.1 → 0.11.1

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.
Files changed (793) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +17 -7
  3. data/README.md +36 -119
  4. data/Rakefile +2 -11
  5. data/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml +12 -8
  6. data/config/action_view_helpers/actionpack/content_security_policy/content_security_policy?.yml +9 -6
  7. data/config/action_view_helpers/actionpack/content_security_policy/content_security_policy_nonce.yml +9 -6
  8. data/config/action_view_helpers/actionpack/polymorphic_routes/polymorphic_path.yml +20 -12
  9. data/config/action_view_helpers/actionpack/polymorphic_routes/polymorphic_url.yml +30 -18
  10. data/config/action_view_helpers/actionpack/request_forgery_protection/form_authenticity_token.yml +15 -9
  11. data/config/action_view_helpers/actionpack/request_forgery_protection/protect_against_forgery?.yml +9 -6
  12. data/config/action_view_helpers/actiontext/content_helper/render_action_text_content.yml +15 -9
  13. data/config/action_view_helpers/actiontext/content_helper/sanitize_action_text_content.yml +15 -9
  14. data/config/action_view_helpers/actiontext/tag_helper/rich_textarea_tag.yml +34 -22
  15. data/config/action_view_helpers/actionview/active_model_helper/error_message.yml +9 -6
  16. data/config/action_view_helpers/actionview/active_model_helper/error_wrapping.yml +9 -6
  17. data/config/action_view_helpers/actionview/active_model_helper/object.yml +9 -6
  18. data/config/action_view_helpers/actionview/asset_tag_helper/apply_stylesheet_media_default.yml +9 -6
  19. data/config/action_view_helpers/actionview/asset_tag_helper/audio_tag.yml +40 -27
  20. data/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml +37 -23
  21. data/config/action_view_helpers/actionview/asset_tag_helper/auto_include_nonce.yml +9 -6
  22. data/config/action_view_helpers/actionview/asset_tag_helper/auto_include_nonce_for_scripts.yml +9 -6
  23. data/config/action_view_helpers/actionview/asset_tag_helper/auto_include_nonce_for_styles.yml +9 -6
  24. data/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml +32 -22
  25. data/config/action_view_helpers/actionview/asset_tag_helper/image_decoding.yml +9 -6
  26. data/config/action_view_helpers/actionview/asset_tag_helper/image_loading.yml +9 -6
  27. data/config/action_view_helpers/actionview/asset_tag_helper/image_tag.yml +67 -46
  28. data/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml +58 -39
  29. data/config/action_view_helpers/actionview/asset_tag_helper/picture_tag.yml +22 -14
  30. data/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml +45 -30
  31. data/config/action_view_helpers/actionview/asset_tag_helper/preload_links_header.yml +9 -6
  32. data/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml +49 -30
  33. data/config/action_view_helpers/actionview/asset_tag_helper/video_tag.yml +53 -35
  34. data/config/action_view_helpers/actionview/asset_url_helper/asset_path.yml +16 -10
  35. data/config/action_view_helpers/actionview/asset_url_helper/asset_url.yml +16 -10
  36. data/config/action_view_helpers/actionview/asset_url_helper/audio_path.yml +16 -10
  37. data/config/action_view_helpers/actionview/asset_url_helper/audio_url.yml +16 -10
  38. data/config/action_view_helpers/actionview/asset_url_helper/compute_asset_extname.yml +9 -6
  39. data/config/action_view_helpers/actionview/asset_url_helper/compute_asset_host.yml +9 -6
  40. data/config/action_view_helpers/actionview/asset_url_helper/compute_asset_path.yml +9 -6
  41. data/config/action_view_helpers/actionview/asset_url_helper/font_path.yml +16 -10
  42. data/config/action_view_helpers/actionview/asset_url_helper/font_url.yml +16 -10
  43. data/config/action_view_helpers/actionview/asset_url_helper/image_path.yml +16 -10
  44. data/config/action_view_helpers/actionview/asset_url_helper/image_url.yml +16 -10
  45. data/config/action_view_helpers/actionview/asset_url_helper/javascript_path.yml +16 -10
  46. data/config/action_view_helpers/actionview/asset_url_helper/javascript_url.yml +16 -10
  47. data/config/action_view_helpers/actionview/asset_url_helper/public_compute_asset_path.yml +9 -6
  48. data/config/action_view_helpers/actionview/asset_url_helper/stylesheet_path.yml +16 -10
  49. data/config/action_view_helpers/actionview/asset_url_helper/stylesheet_url.yml +16 -10
  50. data/config/action_view_helpers/actionview/asset_url_helper/video_path.yml +16 -10
  51. data/config/action_view_helpers/actionview/asset_url_helper/video_url.yml +16 -10
  52. data/config/action_view_helpers/actionview/atom_feed_helper/atom_feed.yml +52 -27
  53. data/config/action_view_helpers/actionview/cache_helper/cache.yml +25 -15
  54. data/config/action_view_helpers/actionview/cache_helper/cache_fragment_name.yml +9 -6
  55. data/config/action_view_helpers/actionview/cache_helper/cache_if.yml +20 -12
  56. data/config/action_view_helpers/actionview/cache_helper/cache_unless.yml +20 -12
  57. data/config/action_view_helpers/actionview/cache_helper/caching_predicate.yml +9 -6
  58. data/config/action_view_helpers/actionview/cache_helper/digest_path_from_template.yml +9 -6
  59. data/config/action_view_helpers/actionview/cache_helper/fragment_for.yml +9 -6
  60. data/config/action_view_helpers/actionview/cache_helper/fragment_name_with_digest.yml +9 -6
  61. data/config/action_view_helpers/actionview/cache_helper/read_fragment_for.yml +9 -6
  62. data/config/action_view_helpers/actionview/cache_helper/uncacheable.yml +9 -6
  63. data/config/action_view_helpers/actionview/cache_helper/write_fragment_for.yml +9 -6
  64. data/config/action_view_helpers/actionview/capture_helper/capture.yml +9 -6
  65. data/config/action_view_helpers/actionview/capture_helper/content_for.yml +20 -12
  66. data/config/action_view_helpers/actionview/capture_helper/content_for_predicate.yml +15 -9
  67. data/config/action_view_helpers/actionview/capture_helper/provide.yml +15 -9
  68. data/config/action_view_helpers/actionview/capture_helper/with_output_buffer.yml +9 -6
  69. data/config/action_view_helpers/actionview/content_exfiltration_prevention_helper/prepend_content_exfiltration_prevention.yml +9 -6
  70. data/config/action_view_helpers/actionview/content_exfiltration_prevention_helper/prevent_content_exfiltration.yml +9 -6
  71. data/config/action_view_helpers/actionview/controller_helper/action_name.yml +9 -6
  72. data/config/action_view_helpers/actionview/controller_helper/assign_controller.yml +9 -6
  73. data/config/action_view_helpers/actionview/controller_helper/controller.yml +9 -6
  74. data/config/action_view_helpers/actionview/controller_helper/controller_name.yml +9 -6
  75. data/config/action_view_helpers/actionview/controller_helper/controller_path.yml +9 -6
  76. data/config/action_view_helpers/actionview/controller_helper/cookies.yml +9 -6
  77. data/config/action_view_helpers/actionview/controller_helper/flash.yml +9 -6
  78. data/config/action_view_helpers/actionview/controller_helper/headers.yml +9 -6
  79. data/config/action_view_helpers/actionview/controller_helper/logger.yml +9 -6
  80. data/config/action_view_helpers/actionview/controller_helper/params.yml +9 -6
  81. data/config/action_view_helpers/actionview/controller_helper/request.yml +9 -6
  82. data/config/action_view_helpers/actionview/controller_helper/request_forgery_protection_token.yml +9 -6
  83. data/config/action_view_helpers/actionview/controller_helper/response.yml +9 -6
  84. data/config/action_view_helpers/actionview/controller_helper/session.yml +9 -6
  85. data/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml +18 -11
  86. data/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml +16 -10
  87. data/config/action_view_helpers/actionview/date_helper/date_select.yml +85 -51
  88. data/config/action_view_helpers/actionview/date_helper/datetime_select.yml +65 -39
  89. data/config/action_view_helpers/actionview/date_helper/distance_of_time_in_words.yml +30 -18
  90. data/config/action_view_helpers/actionview/date_helper/relative_time_in_words.yml +35 -21
  91. data/config/action_view_helpers/actionview/date_helper/select_date.yml +50 -30
  92. data/config/action_view_helpers/actionview/date_helper/select_datetime.yml +65 -39
  93. data/config/action_view_helpers/actionview/date_helper/select_day.yml +40 -24
  94. data/config/action_view_helpers/actionview/date_helper/select_hour.yml +50 -30
  95. data/config/action_view_helpers/actionview/date_helper/select_minute.yml +40 -24
  96. data/config/action_view_helpers/actionview/date_helper/select_month.yml +60 -36
  97. data/config/action_view_helpers/actionview/date_helper/select_second.yml +35 -21
  98. data/config/action_view_helpers/actionview/date_helper/select_time.yml +55 -33
  99. data/config/action_view_helpers/actionview/date_helper/select_year.yml +45 -27
  100. data/config/action_view_helpers/actionview/date_helper/time_ago_in_words.yml +21 -13
  101. data/config/action_view_helpers/actionview/date_helper/time_select.yml +60 -36
  102. data/config/action_view_helpers/actionview/date_helper/time_tag.yml +32 -20
  103. data/config/action_view_helpers/actionview/debug_helper/debug.yml +15 -9
  104. data/config/action_view_helpers/actionview/form_builder/button.yml +60 -0
  105. data/config/action_view_helpers/actionview/form_builder/submit.yml +48 -0
  106. data/config/action_view_helpers/actionview/form_helper/apply_form_for_options.yml +9 -6
  107. data/config/action_view_helpers/actionview/form_helper/checkbox.yml +41 -25
  108. data/config/action_view_helpers/actionview/form_helper/color_field.yml +25 -15
  109. data/config/action_view_helpers/actionview/form_helper/date_field.yml +35 -21
  110. data/config/action_view_helpers/actionview/form_helper/datetime_field.yml +41 -25
  111. data/config/action_view_helpers/actionview/form_helper/email_field.yml +25 -15
  112. data/config/action_view_helpers/actionview/form_helper/emitted_hidden_id.yml +9 -6
  113. data/config/action_view_helpers/actionview/form_helper/fields.yml +37 -18
  114. data/config/action_view_helpers/actionview/form_helper/fields_for.yml +33 -15
  115. data/config/action_view_helpers/actionview/form_helper/file_field.yml +45 -27
  116. data/config/action_view_helpers/actionview/form_helper/form_for.yml +45 -24
  117. data/config/action_view_helpers/actionview/form_helper/form_with.yml +56 -30
  118. data/config/action_view_helpers/actionview/form_helper/form_with_generates_ids.yml +9 -6
  119. data/config/action_view_helpers/actionview/form_helper/form_with_generates_remote_forms.yml +9 -6
  120. data/config/action_view_helpers/actionview/form_helper/hidden_field.yml +25 -15
  121. data/config/action_view_helpers/actionview/form_helper/label.yml +52 -27
  122. data/config/action_view_helpers/actionview/form_helper/month_field.yml +25 -15
  123. data/config/action_view_helpers/actionview/form_helper/multipart.yml +9 -6
  124. data/config/action_view_helpers/actionview/form_helper/multiple_file_field_include_hidden.yml +9 -6
  125. data/config/action_view_helpers/actionview/form_helper/number_field.yml +45 -27
  126. data/config/action_view_helpers/actionview/form_helper/password_field.yml +25 -15
  127. data/config/action_view_helpers/actionview/form_helper/radio_button.yml +30 -18
  128. data/config/action_view_helpers/actionview/form_helper/range_field.yml +45 -27
  129. data/config/action_view_helpers/actionview/form_helper/search_field.yml +25 -15
  130. data/config/action_view_helpers/actionview/form_helper/telephone_field.yml +26 -16
  131. data/config/action_view_helpers/actionview/form_helper/text_field.yml +25 -15
  132. data/config/action_view_helpers/actionview/form_helper/textarea.yml +31 -19
  133. data/config/action_view_helpers/actionview/form_helper/time_field.yml +40 -24
  134. data/config/action_view_helpers/actionview/form_helper/url_field.yml +25 -15
  135. data/config/action_view_helpers/actionview/form_helper/week_field.yml +25 -15
  136. data/config/action_view_helpers/actionview/form_options_helper/collection_checkboxes.yml +58 -31
  137. data/config/action_view_helpers/actionview/form_options_helper/collection_radio_buttons.yml +57 -30
  138. data/config/action_view_helpers/actionview/form_options_helper/collection_select.yml +65 -39
  139. data/config/action_view_helpers/actionview/form_options_helper/grouped_collection_select.yml +65 -39
  140. data/config/action_view_helpers/actionview/form_options_helper/grouped_options_for_select.yml +35 -21
  141. data/config/action_view_helpers/actionview/form_options_helper/option_groups_from_collection_for_select.yml +40 -24
  142. data/config/action_view_helpers/actionview/form_options_helper/options_for_select.yml +20 -12
  143. data/config/action_view_helpers/actionview/form_options_helper/options_from_collection_for_select.yml +30 -18
  144. data/config/action_view_helpers/actionview/form_options_helper/select.yml +60 -36
  145. data/config/action_view_helpers/actionview/form_options_helper/time_zone_options_for_select.yml +25 -15
  146. data/config/action_view_helpers/actionview/form_options_helper/time_zone_select.yml +50 -30
  147. data/config/action_view_helpers/actionview/form_options_helper/weekday_options_for_select.yml +30 -18
  148. data/config/action_view_helpers/actionview/form_options_helper/weekday_select.yml +30 -18
  149. data/config/action_view_helpers/actionview/form_tag_helper/button_tag.yml +39 -21
  150. data/config/action_view_helpers/actionview/form_tag_helper/checkbox_tag.yml +33 -21
  151. data/config/action_view_helpers/actionview/form_tag_helper/color_field_tag.yml +26 -17
  152. data/config/action_view_helpers/actionview/form_tag_helper/date_field_tag.yml +27 -17
  153. data/config/action_view_helpers/actionview/form_tag_helper/datetime_field_tag.yml +28 -18
  154. data/config/action_view_helpers/actionview/form_tag_helper/default_enforce_utf8.yml +9 -6
  155. data/config/action_view_helpers/actionview/form_tag_helper/email_field_tag.yml +27 -17
  156. data/config/action_view_helpers/actionview/form_tag_helper/embed_authenticity_token_in_remote_forms.yml +9 -6
  157. data/config/action_view_helpers/actionview/form_tag_helper/field_id.yml +30 -18
  158. data/config/action_view_helpers/actionview/form_tag_helper/field_name.yml +30 -18
  159. data/config/action_view_helpers/actionview/form_tag_helper/field_set_tag.yml +23 -15
  160. data/config/action_view_helpers/actionview/form_tag_helper/file_field_tag.yml +35 -22
  161. data/config/action_view_helpers/actionview/form_tag_helper/form_tag.yml +38 -24
  162. data/config/action_view_helpers/actionview/form_tag_helper/hidden_field_tag.yml +27 -17
  163. data/config/action_view_helpers/actionview/form_tag_helper/image_submit_tag.yml +31 -21
  164. data/config/action_view_helpers/actionview/form_tag_helper/label_tag.yml +33 -17
  165. data/config/action_view_helpers/actionview/form_tag_helper/month_field_tag.yml +27 -17
  166. data/config/action_view_helpers/actionview/form_tag_helper/number_field_tag.yml +47 -29
  167. data/config/action_view_helpers/actionview/form_tag_helper/password_field_tag.yml +27 -17
  168. data/config/action_view_helpers/actionview/form_tag_helper/radio_button_tag.yml +32 -20
  169. data/config/action_view_helpers/actionview/form_tag_helper/range_field_tag.yml +47 -29
  170. data/config/action_view_helpers/actionview/form_tag_helper/search_field_tag.yml +27 -17
  171. data/config/action_view_helpers/actionview/form_tag_helper/select_tag.yml +47 -29
  172. data/config/action_view_helpers/actionview/form_tag_helper/submit_tag.yml +33 -21
  173. data/config/action_view_helpers/actionview/form_tag_helper/telephone_field_tag.yml +28 -18
  174. data/config/action_view_helpers/actionview/form_tag_helper/text_field_tag.yml +27 -17
  175. data/config/action_view_helpers/actionview/form_tag_helper/textarea_tag.yml +31 -20
  176. data/config/action_view_helpers/actionview/form_tag_helper/time_field_tag.yml +27 -17
  177. data/config/action_view_helpers/actionview/form_tag_helper/url_field_tag.yml +27 -17
  178. data/config/action_view_helpers/actionview/form_tag_helper/utf8_enforcer_tag.yml +9 -6
  179. data/config/action_view_helpers/actionview/form_tag_helper/week_field_tag.yml +28 -18
  180. data/config/action_view_helpers/actionview/javascript_helper/escape_javascript.yml +16 -10
  181. data/config/action_view_helpers/actionview/javascript_helper/javascript_cdata_section.yml +9 -6
  182. data/config/action_view_helpers/actionview/javascript_helper/javascript_tag.yml +41 -27
  183. data/config/action_view_helpers/actionview/number_helper/number_to_currency.yml +29 -18
  184. data/config/action_view_helpers/actionview/number_helper/number_to_human.yml +25 -15
  185. data/config/action_view_helpers/actionview/number_helper/number_to_human_size.yml +20 -12
  186. data/config/action_view_helpers/actionview/number_helper/number_to_percentage.yml +20 -12
  187. data/config/action_view_helpers/actionview/number_helper/number_to_phone.yml +33 -20
  188. data/config/action_view_helpers/actionview/number_helper/number_with_delimiter.yml +21 -13
  189. data/config/action_view_helpers/actionview/number_helper/number_with_precision.yml +25 -15
  190. data/config/action_view_helpers/actionview/output_safety_helper/raw.yml +15 -9
  191. data/config/action_view_helpers/actionview/output_safety_helper/safe_join.yml +20 -12
  192. data/config/action_view_helpers/actionview/output_safety_helper/to_sentence.yml +24 -15
  193. data/config/action_view_helpers/actionview/record_identifier/dom_class.yml +20 -12
  194. data/config/action_view_helpers/actionview/record_identifier/dom_id.yml +20 -12
  195. data/config/action_view_helpers/actionview/record_identifier/dom_target.yml +15 -9
  196. data/config/action_view_helpers/actionview/record_identifier/model_name_from_record_or_class.yml +9 -6
  197. data/config/action_view_helpers/actionview/rendering_helper/render.yml +108 -22
  198. data/config/action_view_helpers/actionview/sanitize_helper/sanitize.yml +25 -15
  199. data/config/action_view_helpers/actionview/sanitize_helper/sanitize_css.yml +15 -9
  200. data/config/action_view_helpers/actionview/sanitize_helper/sanitizer_vendor.yml +9 -6
  201. data/config/action_view_helpers/actionview/sanitize_helper/strip_links.yml +15 -9
  202. data/config/action_view_helpers/actionview/sanitize_helper/strip_tags.yml +15 -9
  203. data/config/action_view_helpers/actionview/tag_helper/cdata_section.yml +15 -9
  204. data/config/action_view_helpers/actionview/tag_helper/content_tag.yml +59 -38
  205. data/config/action_view_helpers/actionview/tag_helper/escape_once.yml +15 -9
  206. data/config/action_view_helpers/actionview/tag_helper/tag.yml +54 -31
  207. data/config/action_view_helpers/actionview/tag_helper/token_list.yml +16 -10
  208. data/config/action_view_helpers/actionview/text_helper/concat.yml +15 -9
  209. data/config/action_view_helpers/actionview/text_helper/current_cycle.yml +15 -9
  210. data/config/action_view_helpers/actionview/text_helper/cycle.yml +23 -14
  211. data/config/action_view_helpers/actionview/text_helper/excerpt.yml +33 -20
  212. data/config/action_view_helpers/actionview/text_helper/highlight.yml +37 -18
  213. data/config/action_view_helpers/actionview/text_helper/pluralize.yml +30 -18
  214. data/config/action_view_helpers/actionview/text_helper/reset_cycle.yml +15 -9
  215. data/config/action_view_helpers/actionview/text_helper/safe_concat.yml +9 -6
  216. data/config/action_view_helpers/actionview/text_helper/simple_format.yml +33 -20
  217. data/config/action_view_helpers/actionview/text_helper/truncate.yml +38 -23
  218. data/config/action_view_helpers/actionview/text_helper/word_wrap.yml +25 -15
  219. data/config/action_view_helpers/actionview/translation_helper/debug_missing_translation.yml +9 -6
  220. data/config/action_view_helpers/actionview/translation_helper/localize.yml +26 -16
  221. data/config/action_view_helpers/actionview/translation_helper/translate.yml +31 -19
  222. data/config/action_view_helpers/actionview/url_helper/button_to.yml +57 -33
  223. data/config/action_view_helpers/actionview/url_helper/button_to_generates_button_tag.yml +9 -6
  224. data/config/action_view_helpers/actionview/url_helper/current_page?.yml +25 -15
  225. data/config/action_view_helpers/actionview/url_helper/link_to.yml +76 -53
  226. data/config/action_view_helpers/actionview/url_helper/link_to_if.yml +54 -20
  227. data/config/action_view_helpers/actionview/url_helper/link_to_unless.yml +54 -20
  228. data/config/action_view_helpers/actionview/url_helper/link_to_unless_current.yml +49 -17
  229. data/config/action_view_helpers/actionview/url_helper/mail_to.yml +60 -34
  230. data/config/action_view_helpers/actionview/url_helper/phone_to.yml +40 -22
  231. data/config/action_view_helpers/actionview/url_helper/sms_to.yml +45 -25
  232. data/config/action_view_helpers/actionview/url_helper/url_for.yml +15 -9
  233. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml +32 -0
  234. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml +39 -0
  235. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml +35 -0
  236. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml +32 -0
  237. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml +35 -0
  238. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache.yml +9 -6
  239. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml +12 -8
  240. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview.yml +9 -6
  241. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml +12 -8
  242. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload.yml +9 -6
  243. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml +12 -8
  244. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml +17 -11
  245. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml +17 -11
  246. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml +20 -12
  247. data/config/action_view_helpers/turbo-rails/frames_helper/turbo_frame_tag.yml +54 -33
  248. data/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml +9 -6
  249. data/config/action_view_helpers/turbo-rails/streams_action_helper/turbo_stream_action_tag.yml +32 -20
  250. data/config/action_view_helpers/turbo-rails/streams_action_helper/turbo_stream_refresh_tag.yml +18 -11
  251. data/config/action_view_helpers/turbo-rails/streams_helper/turbo_stream.yml +9 -6
  252. data/config/action_view_helpers/turbo-rails/streams_helper/turbo_stream_from.yml +28 -18
  253. data/config/html_elements.yml +597 -0
  254. data/config/options.yml +1 -0
  255. data/config/slots/components.yml +52 -0
  256. data/config/state/kind.yml +61 -0
  257. data/config/state/operators.yml +27 -0
  258. data/config/state/predicates.yml +39 -0
  259. data/config/state/transforms.yml +18 -0
  260. data/config/view_context_methods/actionview/base/annotate_rendered_view_with_filenames.yml +4 -4
  261. data/config/view_context_methods/actionview/base/assign.yml +4 -4
  262. data/config/view_context_methods/actionview/base/assigns.yml +4 -4
  263. data/config/view_context_methods/actionview/base/automatically_disable_submit_tag.yml +4 -4
  264. data/config/view_context_methods/actionview/base/compiled_method_container.yml +4 -4
  265. data/config/view_context_methods/actionview/base/config.yml +4 -4
  266. data/config/view_context_methods/actionview/base/default_form_builder.yml +4 -4
  267. data/config/view_context_methods/actionview/base/default_formats.yml +4 -4
  268. data/config/view_context_methods/actionview/base/field_error_proc.yml +4 -4
  269. data/config/view_context_methods/actionview/base/formats.yml +4 -4
  270. data/config/view_context_methods/actionview/base/in_rendering_context.yml +4 -4
  271. data/config/view_context_methods/actionview/base/locale.yml +4 -4
  272. data/config/view_context_methods/actionview/base/lookup_context.yml +4 -4
  273. data/config/view_context_methods/actionview/base/output_buffer.yml +4 -4
  274. data/config/view_context_methods/actionview/base/prefix_partial_path_with_controller_namespace.yml +4 -4
  275. data/config/view_context_methods/actionview/base/remove_hidden_field_autocomplete.yml +4 -4
  276. data/config/view_context_methods/actionview/base/streaming_completion_on_exception.yml +4 -4
  277. data/config/view_context_methods/actionview/base/view_flow.yml +4 -4
  278. data/config/view_context_methods/actionview/base/view_paths.yml +4 -4
  279. data/config/view_context_methods/actionview/base/view_renderer.yml +4 -4
  280. data/config/view_context_methods/actionview/template/local_assigns.yml +4 -4
  281. data/config/view_context_methods/activemodel/conversion/convert_to_model.yml +4 -4
  282. data/config/view_context_methods/activesupport/benchmarkable/benchmark.yml +4 -4
  283. data/config.yml +1249 -786
  284. data/exe/herb +2 -0
  285. data/ext/herb/error_helpers.c +379 -17
  286. data/ext/herb/error_helpers.h +1 -1
  287. data/ext/herb/extconf.rb +4 -2
  288. data/ext/herb/extension.c +137 -74
  289. data/ext/herb/extension_helpers.c +81 -35
  290. data/ext/herb/extension_helpers.h +4 -1
  291. data/ext/herb/nodes.c +859 -374
  292. data/ext/herb/nodes.h +3 -2
  293. data/ext/herb/parser_options_helpers.c +151 -0
  294. data/ext/herb/parser_options_helpers.h +14 -0
  295. data/herb.gemspec +3 -2
  296. data/lib/herb/action_view/helper_registry.rb +1358 -62
  297. data/lib/herb/analysis/partial_declaration.rb +140 -0
  298. data/lib/herb/analysis/partial_index.rb +189 -0
  299. data/lib/herb/analysis/partial_resolution.rb +161 -0
  300. data/lib/herb/analysis/partial_resolver.rb +144 -0
  301. data/lib/herb/analysis/project_index.rb +152 -0
  302. data/lib/herb/{action_view → analysis}/render_analyzer.rb +287 -77
  303. data/lib/herb/analysis/render_graph/ancestor_chain.rb +27 -0
  304. data/lib/herb/analysis/render_graph/builder.rb +376 -0
  305. data/lib/herb/analysis/render_graph/call_frame.rb +9 -0
  306. data/lib/herb/analysis/render_graph/call_site_location.rb +23 -0
  307. data/lib/herb/analysis/render_graph/inferred_signature.rb +21 -0
  308. data/lib/herb/analysis/render_graph/partial_call_site.rb +40 -0
  309. data/lib/herb/analysis/render_graph/partial_context.rb +46 -0
  310. data/lib/herb/analysis/render_graph/template_roots.rb +31 -0
  311. data/lib/herb/analysis/render_graph.rb +285 -0
  312. data/lib/herb/analysis/ruby_locals_index/local.rb +25 -0
  313. data/lib/herb/analysis/ruby_locals_index/named_reference.rb +22 -0
  314. data/lib/herb/analysis/ruby_locals_index/offset_table.rb +44 -0
  315. data/lib/herb/analysis/ruby_locals_index/reference_collector.rb +80 -0
  316. data/lib/herb/analysis/ruby_locals_index/state_locals.rb +49 -0
  317. data/lib/herb/analysis/ruby_locals_index.rb +165 -0
  318. data/lib/herb/analysis/ruby_reference.rb +67 -0
  319. data/lib/herb/analysis/template_dependencies/dependency_collector.rb +179 -0
  320. data/lib/herb/analysis/template_dependencies/node_dependency_collector.rb +309 -0
  321. data/lib/herb/analysis/template_dependencies.rb +562 -0
  322. data/lib/herb/ast/erb_render_node.rb +26 -94
  323. data/lib/herb/ast/helpers.rb +90 -10
  324. data/lib/herb/ast/herb_directive_node.rb +18 -0
  325. data/lib/herb/ast/node.rb +32 -2
  326. data/lib/herb/ast/nodes.rb +1368 -105
  327. data/lib/herb/backend.rb +73 -0
  328. data/lib/herb/bootstrap.rb +8 -8
  329. data/lib/herb/cli.rb +555 -32
  330. data/lib/herb/colors.rb +14 -0
  331. data/lib/herb/configuration.rb +71 -35
  332. data/lib/herb/defaults.yml +2 -1
  333. data/lib/herb/dev/assets.rb +57 -0
  334. data/lib/herb/dev/classifier.rb +84 -0
  335. data/lib/herb/dev/embedded.rb +27 -0
  336. data/lib/herb/dev/pipeline.rb +305 -0
  337. data/lib/herb/dev/protocol.rb +221 -0
  338. data/lib/herb/dev/runner.rb +180 -175
  339. data/lib/herb/dev/server.rb +89 -26
  340. data/lib/herb/dev/server_entry.rb +41 -6
  341. data/lib/herb/dev/watcher.rb +181 -0
  342. data/lib/herb/dev.rb +163 -0
  343. data/lib/herb/diagnostic/formatter.rb +88 -0
  344. data/lib/herb/diagnostic.rb +172 -0
  345. data/lib/herb/diff/operation.rb +36 -0
  346. data/lib/herb/diff/result.rb +62 -0
  347. data/lib/herb/diff.rb +10 -0
  348. data/lib/herb/engine/collector.rb +62 -0
  349. data/lib/herb/engine/compiler.rb +387 -113
  350. data/lib/herb/engine/component_tags/constant_resolver.rb +28 -0
  351. data/lib/herb/engine/component_tags/partial_resolver.rb +54 -0
  352. data/lib/herb/engine/component_tags/resolver.rb +32 -0
  353. data/lib/herb/engine/component_tags/visitor.rb +231 -0
  354. data/lib/herb/engine/css_inliner/inliner.rb +101 -0
  355. data/lib/herb/engine/css_inliner/visitor.rb +174 -0
  356. data/lib/herb/engine/errors.rb +122 -0
  357. data/lib/herb/engine/helpers.rb +82 -0
  358. data/lib/herb/engine/inline_render/inliner.rb +122 -0
  359. data/lib/herb/engine/inline_render/visitor.rb +267 -0
  360. data/lib/herb/engine/runtime/entry.rb +62 -0
  361. data/lib/herb/engine/runtime/error_page.rb +362 -0
  362. data/lib/herb/engine/runtime/html_safe_assertions.rb +217 -0
  363. data/lib/herb/engine/runtime/journal/summary.rb +227 -0
  364. data/lib/herb/engine/runtime/journal.rb +379 -0
  365. data/lib/herb/engine/runtime/middleware.rb +212 -0
  366. data/lib/herb/engine/runtime/observations.rb +73 -0
  367. data/lib/herb/engine/runtime/report.rb +204 -0
  368. data/lib/herb/engine/runtime/session.rb +437 -0
  369. data/lib/herb/engine/runtime.rb +21 -0
  370. data/lib/herb/engine/scoped_style/channel.rb +66 -0
  371. data/lib/herb/engine/scoped_style/collector.rb +74 -0
  372. data/lib/herb/engine/scoped_style/visitor.rb +533 -0
  373. data/lib/herb/engine/slots/annotation.rb +98 -0
  374. data/lib/herb/engine/slots/bindings.rb +61 -0
  375. data/lib/herb/engine/slots/callee.rb +118 -0
  376. data/lib/herb/engine/slots/component_definitions.rb +67 -0
  377. data/lib/herb/engine/slots/components/async.rb +24 -0
  378. data/lib/herb/engine/slots/components/base.rb +132 -0
  379. data/lib/herb/engine/slots/components/deferred.rb +81 -0
  380. data/lib/herb/engine/slots/components/fallback.rb +27 -0
  381. data/lib/herb/engine/slots/components/fragment.rb +99 -0
  382. data/lib/herb/engine/slots/components/lazy.rb +24 -0
  383. data/lib/herb/engine/slots/components.rb +52 -0
  384. data/lib/herb/engine/slots/dependencies/channel.rb +72 -0
  385. data/lib/herb/engine/slots/dependencies.rb +269 -0
  386. data/lib/herb/engine/slots/dynamics_compiler.rb +864 -0
  387. data/lib/herb/engine/slots/identifier.rb +35 -0
  388. data/lib/herb/engine/slots/manifest/channel.rb +74 -0
  389. data/lib/herb/engine/slots/manifest/collector.rb +75 -0
  390. data/lib/herb/engine/slots/markers.rb +152 -0
  391. data/lib/herb/engine/slots/render_bindings.rb +141 -0
  392. data/lib/herb/engine/slots/schema_compiler.rb +94 -0
  393. data/lib/herb/engine/slots/state_anchor.rb +94 -0
  394. data/lib/herb/engine/slots/state_compiler.rb +1325 -0
  395. data/lib/herb/engine/slots/state_directives.rb +854 -0
  396. data/lib/herb/engine/slots/state_kinds.rb +54 -0
  397. data/lib/herb/engine/slots/state_operators.rb +36 -0
  398. data/lib/herb/engine/slots/state_overrides.rb +79 -0
  399. data/lib/herb/engine/slots/state_predicates.rb +65 -0
  400. data/lib/herb/engine/slots/state_transforms.rb +42 -0
  401. data/lib/herb/engine/slots/statics.rb +185 -0
  402. data/lib/herb/engine/slots/subtree.rb +82 -0
  403. data/lib/herb/engine/slots/subtree_compiler.rb +255 -0
  404. data/lib/herb/engine/slots/types.rb +42 -0
  405. data/lib/herb/engine/slots/visitor.rb +2267 -0
  406. data/lib/herb/engine/validators/accessibility_validator.rb +3 -3
  407. data/lib/herb/engine/validators/base.rb +36 -0
  408. data/lib/herb/engine/validators/generator_template_validator.rb +39 -0
  409. data/lib/herb/engine/validators/nesting_validator.rb +3 -3
  410. data/lib/herb/engine/validators/render_validator.rb +13 -33
  411. data/lib/herb/engine/validators/security_validator.rb +3 -17
  412. data/lib/herb/engine/validators.rb +43 -0
  413. data/lib/herb/engine/visitors/auto_close_omitted_tags_visitor.rb +48 -0
  414. data/lib/herb/engine/visitors/content_for_visitor.rb +127 -0
  415. data/lib/herb/engine/{debug_visitor.rb → visitors/debug_visitor.rb} +122 -94
  416. data/lib/herb/engine/visitors/html_safe_assertions_visitor.rb +281 -0
  417. data/lib/herb/engine/visitors/instrumentation_visitor.rb +290 -0
  418. data/lib/herb/engine/visitors/optimize_visitor.rb +728 -0
  419. data/lib/herb/engine/visitors/remove_comments_visitor.rb +106 -0
  420. data/lib/herb/engine/visitors/source_attribution_visitor.rb +177 -0
  421. data/lib/herb/engine.rb +218 -259
  422. data/lib/herb/ensure_installed.rb +43 -0
  423. data/lib/herb/errors.rb +681 -153
  424. data/lib/herb/fingerprint.rb +82 -0
  425. data/lib/herb/html/elements.rb +18 -0
  426. data/lib/herb/html/util.rb +21 -2
  427. data/lib/herb/lex_result.rb +0 -10
  428. data/lib/herb/locate.rb +163 -0
  429. data/lib/herb/location.rb +20 -0
  430. data/lib/herb/parse_result.rb +17 -11
  431. data/lib/herb/parser_options.rb +69 -40
  432. data/lib/herb/position.rb +19 -0
  433. data/lib/herb/prism_inspect.rb +1 -4
  434. data/lib/herb/project.rb +39 -23
  435. data/lib/herb/range.rb +5 -0
  436. data/lib/herb/result.rb +2 -2
  437. data/lib/herb/ruby_program.rb +110 -0
  438. data/lib/herb/source_path.rb +232 -0
  439. data/lib/herb/token.rb +14 -3
  440. data/lib/herb/version.rb +1 -1
  441. data/lib/herb/visitor/context/origin.rb +52 -0
  442. data/lib/herb/visitor/context/replacements.rb +59 -0
  443. data/lib/herb/visitor/context.rb +169 -0
  444. data/lib/herb/visitor/context_aware.rb +45 -0
  445. data/lib/herb/visitor/diagnostics.rb +148 -0
  446. data/lib/herb/visitor/experimental.rb +33 -0
  447. data/lib/herb/visitor/parser_option_requirements.rb +151 -0
  448. data/lib/herb/visitor/stack.rb +192 -0
  449. data/lib/herb/visitor.rb +47 -1
  450. data/lib/herb/warnings.rb +25 -0
  451. data/lib/herb.rb +74 -63
  452. data/sig/herb/action_view/helper_registry.rbs +82 -10
  453. data/sig/herb/analysis/partial_declaration.rbs +80 -0
  454. data/sig/herb/analysis/partial_index.rbs +66 -0
  455. data/sig/herb/analysis/partial_resolution.rbs +57 -0
  456. data/sig/herb/analysis/partial_resolver.rbs +76 -0
  457. data/sig/herb/analysis/project_index.rbs +57 -0
  458. data/sig/herb/{action_view → analysis}/render_analyzer.rbs +25 -4
  459. data/sig/herb/analysis/render_graph/ancestor_chain.rbs +22 -0
  460. data/sig/herb/analysis/render_graph/builder.rbs +180 -0
  461. data/sig/herb/analysis/render_graph/call_frame.rbs +26 -0
  462. data/sig/herb/analysis/render_graph/call_site_location.rbs +28 -0
  463. data/sig/herb/analysis/render_graph/inferred_signature.rbs +27 -0
  464. data/sig/herb/analysis/render_graph/partial_call_site.rbs +36 -0
  465. data/sig/herb/analysis/render_graph/partial_context.rbs +31 -0
  466. data/sig/herb/analysis/render_graph/template_roots.rbs +32 -0
  467. data/sig/herb/analysis/render_graph.rbs +83 -0
  468. data/sig/herb/analysis/ruby_locals_index/local.rbs +21 -0
  469. data/sig/herb/analysis/ruby_locals_index/named_reference.rbs +20 -0
  470. data/sig/herb/analysis/ruby_locals_index/offset_table.rbs +26 -0
  471. data/sig/herb/analysis/ruby_locals_index/reference_collector.rbs +40 -0
  472. data/sig/herb/analysis/ruby_locals_index/state_locals.rbs +19 -0
  473. data/sig/herb/analysis/ruby_locals_index.rbs +62 -0
  474. data/sig/herb/analysis/ruby_reference.rbs +28 -0
  475. data/sig/herb/analysis/template_dependencies/dependency_collector.rbs +52 -0
  476. data/sig/herb/analysis/template_dependencies/node_dependency_collector.rbs +81 -0
  477. data/sig/herb/analysis/template_dependencies.rbs +125 -0
  478. data/sig/herb/ast/erb_render_node.rbs +10 -2
  479. data/sig/herb/ast/helpers.rbs +31 -1
  480. data/sig/herb/ast/herb_directive_node.rbs +13 -0
  481. data/sig/herb/ast/node.rbs +9 -0
  482. data/sig/herb/ast/nodes.rbs +668 -89
  483. data/sig/herb/backend.rbs +52 -0
  484. data/sig/herb/bootstrap.rbs +2 -0
  485. data/sig/herb/colors.rbs +3 -0
  486. data/sig/herb/configuration.rbs +27 -3
  487. data/sig/herb/dev/assets.rbs +27 -0
  488. data/sig/herb/dev/classifier.rbs +57 -0
  489. data/sig/herb/dev/embedded.rbs +21 -0
  490. data/sig/herb/dev/pipeline.rbs +118 -0
  491. data/sig/herb/dev/protocol.rbs +78 -0
  492. data/sig/herb/dev/runner.rbs +64 -23
  493. data/sig/herb/dev/server.rbs +49 -22
  494. data/sig/herb/dev/server_entry.rbs +46 -21
  495. data/sig/herb/dev/watcher.rbs +84 -0
  496. data/sig/herb/dev.rbs +58 -0
  497. data/sig/herb/diagnostic/formatter.rbs +28 -0
  498. data/sig/herb/diagnostic.rbs +88 -0
  499. data/sig/herb/diff/operation.rbs +36 -0
  500. data/sig/herb/diff/result.rbs +36 -0
  501. data/sig/herb/diff.rbs +6 -0
  502. data/sig/herb/engine/collector.rbs +48 -0
  503. data/sig/herb/engine/compiler.rbs +74 -24
  504. data/sig/herb/engine/component_tags/constant_resolver.rbs +19 -0
  505. data/sig/herb/engine/component_tags/partial_resolver.rbs +30 -0
  506. data/sig/herb/engine/component_tags/resolver.rbs +25 -0
  507. data/sig/herb/engine/component_tags/visitor.rbs +69 -0
  508. data/sig/herb/engine/css_inliner/inliner.rbs +65 -0
  509. data/sig/herb/engine/css_inliner/visitor.rbs +87 -0
  510. data/sig/herb/engine/errors.rbs +68 -0
  511. data/sig/herb/engine/helpers.rbs +28 -0
  512. data/sig/herb/engine/inline_render/inliner.rbs +39 -0
  513. data/sig/herb/engine/inline_render/visitor.rbs +104 -0
  514. data/sig/herb/engine/runtime/entry.rbs +43 -0
  515. data/sig/herb/engine/runtime/error_page.rbs +115 -0
  516. data/sig/herb/engine/runtime/html_safe_assertions.rbs +99 -0
  517. data/sig/herb/engine/runtime/journal/summary.rbs +172 -0
  518. data/sig/herb/engine/runtime/journal.rbs +126 -0
  519. data/sig/herb/engine/runtime/middleware.rbs +96 -0
  520. data/sig/herb/engine/runtime/observations.rbs +38 -0
  521. data/sig/herb/engine/runtime/report.rbs +97 -0
  522. data/sig/herb/engine/runtime/session.rbs +233 -0
  523. data/sig/herb/engine/runtime.rbs +14 -0
  524. data/sig/herb/engine/scoped_style/channel.rbs +45 -0
  525. data/sig/herb/engine/scoped_style/collector.rbs +45 -0
  526. data/sig/herb/engine/scoped_style/visitor.rbs +220 -0
  527. data/sig/herb/engine/slots/annotation.rbs +72 -0
  528. data/sig/herb/engine/slots/bindings.rbs +34 -0
  529. data/sig/herb/engine/slots/callee.rbs +62 -0
  530. data/sig/herb/engine/slots/component_definitions.rbs +19 -0
  531. data/sig/herb/engine/slots/components/async.rbs +20 -0
  532. data/sig/herb/engine/slots/components/base.rbs +60 -0
  533. data/sig/herb/engine/slots/components/deferred.rbs +29 -0
  534. data/sig/herb/engine/slots/components/fallback.rbs +21 -0
  535. data/sig/herb/engine/slots/components/fragment.rbs +34 -0
  536. data/sig/herb/engine/slots/components/lazy.rbs +20 -0
  537. data/sig/herb/engine/slots/components.rbs +30 -0
  538. data/sig/herb/engine/slots/dependencies/channel.rbs +51 -0
  539. data/sig/herb/engine/slots/dependencies.rbs +94 -0
  540. data/sig/herb/engine/slots/dynamics_compiler.rbs +352 -0
  541. data/sig/herb/engine/slots/identifier.rbs +19 -0
  542. data/sig/herb/engine/slots/manifest/channel.rbs +49 -0
  543. data/sig/herb/engine/slots/manifest/collector.rbs +45 -0
  544. data/sig/herb/engine/slots/markers.rbs +99 -0
  545. data/sig/herb/engine/slots/render_bindings.rbs +69 -0
  546. data/sig/herb/engine/slots/schema_compiler.rbs +62 -0
  547. data/sig/herb/engine/slots/state_anchor.rbs +41 -0
  548. data/sig/herb/engine/slots/state_compiler.rbs +258 -0
  549. data/sig/herb/engine/slots/state_directives.rbs +278 -0
  550. data/sig/herb/engine/slots/state_kinds.rbs +24 -0
  551. data/sig/herb/engine/slots/state_operators.rbs +19 -0
  552. data/sig/herb/engine/slots/state_overrides.rbs +33 -0
  553. data/sig/herb/engine/slots/state_predicates.rbs +16 -0
  554. data/sig/herb/engine/slots/state_transforms.rbs +15 -0
  555. data/sig/herb/engine/slots/statics.rbs +67 -0
  556. data/sig/herb/engine/slots/subtree.rbs +49 -0
  557. data/sig/herb/engine/slots/subtree_compiler.rbs +130 -0
  558. data/sig/herb/engine/slots/types.rbs +46 -0
  559. data/sig/herb/engine/slots/visitor.rbs +603 -0
  560. data/sig/herb/engine/validators/accessibility_validator.rbs +1 -1
  561. data/sig/herb/engine/validators/base.rbs +28 -0
  562. data/sig/herb/engine/validators/generator_template_validator.rbs +19 -0
  563. data/sig/herb/engine/validators/nesting_validator.rbs +1 -1
  564. data/sig/herb/engine/validators/render_validator.rbs +2 -6
  565. data/sig/herb/engine/validators/security_validator.rbs +1 -3
  566. data/sig/herb/engine/validators.rbs +21 -0
  567. data/sig/herb/engine/visitors/auto_close_omitted_tags_visitor.rbs +26 -0
  568. data/sig/herb/engine/visitors/content_for_visitor.rbs +52 -0
  569. data/sig/herb/engine/{debug_visitor.rbs → visitors/debug_visitor.rbs} +26 -15
  570. data/sig/herb/engine/visitors/html_safe_assertions_visitor.rbs +100 -0
  571. data/sig/herb/engine/visitors/instrumentation_visitor.rbs +111 -0
  572. data/sig/herb/engine/visitors/optimize_visitor.rbs +246 -0
  573. data/sig/herb/engine/visitors/remove_comments_visitor.rbs +66 -0
  574. data/sig/herb/engine/visitors/source_attribution_visitor.rbs +90 -0
  575. data/sig/herb/engine.rbs +47 -41
  576. data/sig/herb/ensure_installed.rbs +9 -0
  577. data/sig/herb/errors.rbs +311 -0
  578. data/sig/herb/fingerprint.rbs +37 -0
  579. data/sig/herb/html/elements.rbs +19 -0
  580. data/sig/herb/html/util.rbs +15 -1
  581. data/sig/herb/lex_result.rbs +0 -6
  582. data/sig/herb/locate.rbs +89 -0
  583. data/sig/herb/location.rbs +12 -0
  584. data/sig/herb/parse_result.rbs +10 -8
  585. data/sig/herb/parser_options.rbs +53 -19
  586. data/sig/herb/position.rbs +11 -0
  587. data/sig/herb/range.rbs +3 -0
  588. data/sig/herb/ruby_program.rbs +51 -0
  589. data/sig/herb/source_path.rbs +132 -0
  590. data/sig/herb/token.rbs +5 -2
  591. data/sig/herb/visitor/context/origin.rbs +51 -0
  592. data/sig/herb/visitor/context/replacements.rbs +39 -0
  593. data/sig/herb/visitor/context.rbs +83 -0
  594. data/sig/herb/visitor/context_aware.rbs +32 -0
  595. data/sig/herb/visitor/diagnostics.rbs +82 -0
  596. data/sig/herb/visitor/experimental.rbs +23 -0
  597. data/sig/herb/visitor/parser_option_requirements.rbs +76 -0
  598. data/sig/herb/visitor/stack.rbs +91 -0
  599. data/sig/herb/visitor.rbs +22 -0
  600. data/sig/herb/warnings.rbs +10 -0
  601. data/sig/herb.rbs +27 -10
  602. data/sig/manifest.yaml +13 -0
  603. data/sig/serialized_ast_errors.rbs +62 -1
  604. data/sig/serialized_ast_nodes.rbs +59 -2
  605. data/src/analyze/action_view/attribute_extraction_helpers.c +73 -36
  606. data/src/analyze/action_view/generated_handlers.c +37 -1
  607. data/src/analyze/action_view/generated_handlers.h +2 -1
  608. data/src/analyze/action_view/helper_registry.c +1353 -58
  609. data/src/analyze/action_view/registry.c +10 -17
  610. data/src/analyze/action_view/stylesheet_link_tag.c +40 -0
  611. data/src/analyze/action_view/tag_helper_node_builders.c +63 -3
  612. data/src/analyze/action_view/tag_helpers.c +502 -111
  613. data/src/analyze/analyze.c +306 -89
  614. data/src/analyze/analyze_helpers.c +502 -237
  615. data/src/analyze/analyzed_ruby.c +2 -0
  616. data/src/analyze/builders.c +80 -51
  617. data/src/analyze/conditional_elements.c +13 -8
  618. data/src/analyze/conditional_open_tags.c +11 -5
  619. data/src/analyze/control_type.c +36 -13
  620. data/src/analyze/herb_directives.c +701 -0
  621. data/src/analyze/invalid_structures.c +11 -7
  622. data/src/analyze/iteration_nodes.c +306 -0
  623. data/src/analyze/missing_end.c +116 -32
  624. data/src/analyze/parse_errors.c +8 -3
  625. data/src/analyze/postfix_conditionals.c +98 -63
  626. data/src/analyze/prism_annotate.c +45 -13
  627. data/src/analyze/render_nodes.c +348 -123
  628. data/src/analyze/strict_locals.c +30 -26
  629. data/src/analyze/ternary_conditionals.c +95 -70
  630. data/src/analyze/transform.c +43 -1
  631. data/src/ast/ast_node.c +3 -13
  632. data/src/ast/ast_nodes.c +914 -113
  633. data/src/ast/ast_pretty_print.c +166 -3
  634. data/src/diff/herb_diff.c +8 -0
  635. data/src/diff/herb_diff_attributes.c +1 -0
  636. data/src/diff/herb_diff_children.c +4 -3
  637. data/src/diff/herb_diff_helpers.c +7 -1
  638. data/src/diff/herb_diff_nodes.c +186 -71
  639. data/src/diff/herb_hash_tree.c +99 -1
  640. data/src/errors.c +1124 -105
  641. data/src/extract.c +227 -144
  642. data/src/herb.c +43 -8
  643. data/src/include/analyze/action_view/attribute_extraction_helpers.h +1 -1
  644. data/src/include/analyze/action_view/helper_registry.h +28 -4
  645. data/src/include/analyze/action_view/tag_helper_handler.h +1 -1
  646. data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
  647. data/src/include/analyze/analyze.h +11 -22
  648. data/src/include/analyze/analyzed_ruby.h +25 -0
  649. data/src/include/analyze/builders.h +16 -0
  650. data/src/include/analyze/helpers.h +25 -24
  651. data/src/include/analyze/herb_directive_kinds.h +41 -0
  652. data/src/include/analyze/herb_directives.h +14 -0
  653. data/src/include/analyze/iteration_nodes.h +11 -0
  654. data/src/include/analyze/prism_annotate.h +3 -3
  655. data/src/include/analyze/ternary_conditionals.h +2 -0
  656. data/src/include/ast/ast_node.h +1 -5
  657. data/src/include/ast/ast_nodes.h +78 -3
  658. data/src/include/ast/ast_pretty_print.h +1 -1
  659. data/src/include/diff/herb_diff.h +11 -0
  660. data/src/include/errors.h +163 -38
  661. data/src/include/extract.h +11 -0
  662. data/src/include/herb.h +6 -0
  663. data/src/include/lexer/erb_openers.h +29 -0
  664. data/src/include/lexer/lexer.h +1 -0
  665. data/src/include/lexer/lexer_peek_helpers.h +8 -1
  666. data/src/include/lexer/lexer_struct.h +4 -0
  667. data/src/include/lexer/token.h +5 -0
  668. data/src/include/lexer/token_struct.h +8 -6
  669. data/src/include/lib/hb_allocator.h +2 -1
  670. data/src/include/lib/hb_array.h +2 -0
  671. data/src/include/lib/hb_clock.h +15 -0
  672. data/src/include/lib/hb_foreach.h +1 -1
  673. data/src/include/lib/hb_string.h +7 -0
  674. data/src/include/parser/dot_notation.h +1 -1
  675. data/src/include/parser/foreign_content_elements.h +32 -0
  676. data/src/include/parser/parser.h +40 -26
  677. data/src/include/parser/parser_helpers.h +13 -11
  678. data/src/include/parser/parser_options.h +41 -0
  679. data/src/include/prism/herb_prism_node.h +6 -1
  680. data/src/include/prism/ruby_parser.h +6 -0
  681. data/src/include/util/html_elements.h +85 -0
  682. data/src/include/util/html_util.h +1 -0
  683. data/src/include/version.h +1 -1
  684. data/src/lexer/lexer_peek_helpers.c +15 -1
  685. data/src/lexer/token.c +89 -1
  686. data/src/lexer.c +228 -12
  687. data/src/lib/hb_allocator.c +2 -0
  688. data/src/lib/hb_array.c +26 -0
  689. data/src/lib/hb_string.c +26 -0
  690. data/src/main.c +1 -1
  691. data/src/parser/dot_notation.c +4 -3
  692. data/src/parser/match_tags.c +66 -1
  693. data/src/parser/parser_helpers.c +69 -37
  694. data/src/parser.c +658 -385
  695. data/src/parser_options_defaults.c +30 -0
  696. data/src/prism/prism_helpers.c +94 -0
  697. data/src/prism/ruby_parser.c +49 -0
  698. data/src/util/html_util.c +11 -58
  699. data/src/visitor.c +77 -1
  700. data/templates/ext/herb/error_helpers.c.erb +1 -1
  701. data/templates/ext/herb/nodes.c.erb +19 -14
  702. data/templates/ext/herb/nodes.h.erb +2 -1
  703. data/templates/ext/herb/parser_options_helpers.c.erb +69 -0
  704. data/templates/ext/herb/parser_options_helpers.h.erb +11 -0
  705. data/templates/java/nodes.c.erb +21 -17
  706. data/templates/java/nodes.h.erb +3 -2
  707. data/templates/java/org/herb/ParserOptions.java.erb +24 -0
  708. data/templates/java/org/herb/ast/HelperRegistry.java.erb +67 -31
  709. data/templates/java/org/herb/ast/HelperType.java.erb +29 -0
  710. data/templates/java/org/herb/ast/Nodes.java.erb +11 -7
  711. data/templates/java/parser_options_helpers.c.erb +45 -0
  712. data/templates/java/parser_options_helpers.h.erb +10 -0
  713. data/templates/javascript/packages/client/src/state-kinds.ts.erb +49 -0
  714. data/templates/javascript/packages/client/src/state-operators.ts.erb +15 -0
  715. data/templates/javascript/packages/client/src/state-predicates.ts.erb +27 -0
  716. data/templates/javascript/packages/client/src/state-transforms.ts.erb +19 -0
  717. data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +47 -0
  718. data/templates/javascript/packages/core/src/built-in-components.ts.erb +67 -0
  719. data/templates/javascript/packages/core/src/errors.ts.erb +9 -3
  720. data/templates/javascript/packages/core/src/foreign-content-elements.ts.erb +18 -0
  721. data/templates/javascript/packages/core/src/html-constants.ts.erb +32 -0
  722. data/templates/javascript/packages/core/src/html-elements.ts.erb +41 -0
  723. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +11 -7
  724. data/templates/javascript/packages/core/src/nodes.ts.erb +103 -20
  725. data/templates/javascript/packages/core/src/parser-options.ts.erb +41 -0
  726. data/templates/javascript/packages/core/src/visitor.ts.erb +1 -1
  727. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +3 -3
  728. data/templates/javascript/packages/node/extension/nodes.cpp.erb +18 -13
  729. data/templates/javascript/packages/node/extension/nodes.h.erb +3 -3
  730. data/templates/javascript/packages/node/extension/parser_options_helpers.cpp.erb +41 -0
  731. data/templates/javascript/packages/node/extension/parser_options_helpers.h.erb +12 -0
  732. data/templates/lib/herb/action_view/helper_registry.rb.erb +77 -3
  733. data/templates/lib/herb/ast/nodes.rb.erb +16 -6
  734. data/templates/lib/herb/engine/slots/component_definitions.rb.erb +38 -0
  735. data/templates/lib/herb/engine/slots/state_kinds.rb.erb +37 -0
  736. data/templates/lib/herb/engine/slots/state_operators.rb.erb +26 -0
  737. data/templates/lib/herb/engine/slots/state_predicates.rb.erb +39 -0
  738. data/templates/lib/herb/engine/slots/state_transforms.rb.erb +27 -0
  739. data/templates/lib/herb/errors.rb.erb +34 -6
  740. data/templates/lib/herb/html/elements.rb.erb +12 -0
  741. data/templates/lib/herb/parser_options.rb.erb +32 -0
  742. data/templates/lib/herb/visitor.rb.erb +7 -1
  743. data/templates/rust/herb-printer/src/printer_visitor.rs.erb +14 -0
  744. data/templates/rust/src/action_view_helpers.rs.erb +38 -0
  745. data/templates/rust/src/ast/nodes.rs.erb +21 -31
  746. data/templates/rust/src/errors.rs.erb +1 -1
  747. data/templates/rust/src/nodes.rs.erb +58 -26
  748. data/templates/rust/src/parser_options.rs.erb +16 -0
  749. data/templates/rust/src/prism/generated_deserialize.rs.erb +109 -0
  750. data/templates/rust/src/prism/generated_prettyprint.rs.erb +153 -0
  751. data/templates/rust/src/union_types.rs.erb +7 -1
  752. data/templates/rust/src/visitor.rs.erb +1 -1
  753. data/templates/src/analyze/action_view/helper_registry.c.erb +22 -0
  754. data/templates/src/analyze/missing_end.c.erb +13 -5
  755. data/templates/src/ast/ast_nodes.c.erb +41 -2
  756. data/templates/src/diff/herb_diff_nodes.c.erb +44 -8
  757. data/templates/src/errors.c.erb +8 -3
  758. data/templates/src/include/analyze/action_view/helper_registry.h.erb +19 -0
  759. data/templates/src/include/analyze/herb_directive_kinds.h.erb +25 -0
  760. data/templates/src/include/errors.h.erb +3 -1
  761. data/templates/src/include/parser/foreign_content_elements.h.erb +23 -0
  762. data/templates/src/include/parser/parser_options.h.erb +21 -0
  763. data/templates/src/include/util/html_elements.h.erb +28 -0
  764. data/templates/src/parser_options_defaults.c.erb +9 -0
  765. data/templates/template.rb +564 -9
  766. data/templates/wasm/error_helpers.cpp.erb +4 -4
  767. data/templates/wasm/nodes.cpp.erb +17 -27
  768. data/templates/wasm/nodes.h.erb +3 -3
  769. data/templates/wasm/parser_options_helpers.cpp.erb +25 -0
  770. data/templates/wasm/parser_options_helpers.h.erb +12 -0
  771. metadata +309 -30
  772. data/config/action_view_helpers/actionview/form_helper/button.yml +0 -38
  773. data/config/action_view_helpers/actionview/form_helper/submit.yml +0 -35
  774. data/lib/herb/ast.rb +0 -7
  775. data/lib/herb/diff_operation.rb +0 -34
  776. data/lib/herb/diff_result.rb +0 -59
  777. data/lib/herb/engine/error_formatter.rb +0 -432
  778. data/lib/herb/engine/parser_error_overlay.rb +0 -771
  779. data/lib/herb/engine/validation_error_overlay.rb +0 -182
  780. data/lib/herb/engine/validation_errors.rb +0 -65
  781. data/lib/herb/engine/validator.rb +0 -80
  782. data/sig/herb/ast.rbs +0 -6
  783. data/sig/herb/diff_operation.rbs +0 -34
  784. data/sig/herb/diff_result.rbs +0 -34
  785. data/sig/herb/engine/debug.rbs +0 -38
  786. data/sig/herb/engine/error_formatter.rbs +0 -47
  787. data/sig/herb/engine/parser_error_overlay.rbs +0 -41
  788. data/sig/herb/engine/validation_error_overlay.rbs +0 -35
  789. data/sig/herb/engine/validation_errors.rbs +0 -45
  790. data/sig/herb/engine/validator.rbs +0 -41
  791. data/sig/herb_c_extension.rbs +0 -10
  792. data/sig/vendor/did_you_mean.rbs +0 -6
  793. data/sig/vendor/parallel.rbs +0 -4
data/lib/herb/cli.rb CHANGED
@@ -5,10 +5,15 @@
5
5
 
6
6
  require "optparse"
7
7
 
8
+ require_relative "../herb"
9
+ require_relative "project"
10
+ require_relative "configuration"
11
+ require_relative "engine/slots/visitor"
12
+
8
13
  class Herb::CLI
9
14
  include Herb::Colors
10
15
 
11
- attr_accessor :json, :silent, :log_file, :no_timing, :local, :escape, :no_escape, :freeze, :debug, :tool, :strict, :analyze, :track_whitespace, :verbose, :isolate, :arena_stats, :leak_check, :action_view_helpers, :trim, :optimize
16
+ attr_accessor :json, :silent, :log_file, :no_timing, :local, :escape, :no_escape, :freeze, :debug, :tool, :strict, :analyze, :track_whitespace, :track_locations, :verbose, :isolate, :arena_stats, :leak_check, :action_view_helpers, :no_trim, :optimize, :slots, :scoped_styles, :inline_css, :file_timeout
12
17
 
13
18
  def initialize(args)
14
19
  @args = args
@@ -84,7 +89,7 @@ class Herb::CLI
84
89
 
85
90
  def help(exit_code = 0)
86
91
  message = <<~HELP
87
- Herb 🌿 Powerful and seamless HTML-aware ERB toolchain.
92
+ Herb 🌿 A modern templating language for the HTML+ERB you already have.
88
93
 
89
94
  Usage:
90
95
  bundle exec herb [command] [options]
@@ -107,6 +112,7 @@ class Herb::CLI
107
112
  bundle exec herb actionview check [path] Check if render calls resolve to valid partial files.
108
113
  bundle exec herb actionview graph [path] Show render dependency graph for a project or file.
109
114
  bundle exec herb actionview render [file] Render ERB template using ActionView helpers.
115
+ bundle exec herb actionview context [file] Show what a partial is rendered inside of.
110
116
 
111
117
  bundle exec herb lint [patterns] Lint templates (delegates to @herb-tools/linter)
112
118
  bundle exec herb format [patterns] Format templates (delegates to @herb-tools/formatter)
@@ -154,6 +160,7 @@ class Herb::CLI
154
160
  project.silent = silent
155
161
  project.verbose = verbose || ci?
156
162
  project.isolate = isolate
163
+ project.file_timeout = file_timeout if file_timeout
157
164
  project.validate_ruby = true
158
165
  project.arena_stats = arena_stats
159
166
  project.leak_check = leak_check
@@ -166,7 +173,7 @@ class Herb::CLI
166
173
  show_config
167
174
  exit(0)
168
175
  when "parse"
169
- Herb.parse(file_content, strict: strict.nil? || strict, analyze: analyze.nil? || analyze, track_whitespace: track_whitespace || false, arena_stats: arena_stats, action_view_helpers: action_view_helpers || false)
176
+ Herb.parse(file_content, strict: strict.nil? || strict, analyze: analyze.nil? || analyze, track_whitespace: track_whitespace || false, track_locations: track_locations.nil? || track_locations, arena_stats: arena_stats, action_view_helpers: action_view_helpers || false)
170
177
  when "compile"
171
178
  compile_template
172
179
  when "render"
@@ -180,12 +187,7 @@ class Herb::CLI
180
187
  puts Herb.extract_html(file_content)
181
188
  exit(0)
182
189
  when "playground"
183
- require "bundler/inline"
184
-
185
- gemfile do
186
- source "https://rubygems.org"
187
- gem "lz_string"
188
- end
190
+ Herb.ensure_installed("lz_string")
189
191
 
190
192
  hash = LZString::UriSafe.compress(file_content)
191
193
  local_url = "http://localhost:5173"
@@ -269,6 +271,10 @@ class Herb::CLI
269
271
  self.isolate = true
270
272
  end
271
273
 
274
+ parser.on("--timeout SECONDS", Float, "Per-file timeout for parse + compile (for analyze command) (default: #{Herb::Project::DEFAULT_FILE_TIMEOUT})") do |seconds|
275
+ self.file_timeout = seconds
276
+ end
277
+
272
278
  parser.on("--log-file", "Enable log file generation") do
273
279
  self.log_file = true
274
280
  end
@@ -317,18 +323,45 @@ class Herb::CLI
317
323
  self.track_whitespace = true
318
324
  end
319
325
 
326
+ parser.on("--track-locations", "Enable source location tracking (for parse command) (default: true)") do
327
+ self.track_locations = true
328
+ end
329
+
330
+ parser.on("--no-track-locations", "Disable source location tracking (for parse command)") do
331
+ self.track_locations = false
332
+ end
333
+
320
334
  parser.on("--action-view-helpers", "Enable Action View helper detection (for parse command) (default: false)") do
321
335
  self.action_view_helpers = true
322
336
  end
323
337
 
324
- parser.on("--trim", "Enable trimming of leading/trailing whitespace (for compile/render commands)") do
325
- self.trim = true
338
+ parser.on("--no-trim", "Disable whitespace trimming around standalone ERB tags (for compile/render commands)") do
339
+ self.no_trim = true
326
340
  end
327
341
 
328
- parser.on("--optimize", "Enable compile-time optimizations for Action View helpers (for compile/render commands) (default: false)") do
342
+ parser.on("--optimize", "Enable compile-time optimizations for helpers and static output (for compile/render commands) (default: false)") do
329
343
  self.optimize = true
330
344
  end
331
345
 
346
+ parser.on("--scoped-styles", "Scope each `<style scoped>` block to its file with Lightning CSS (for compile/render commands) (default: false)") do
347
+ self.scoped_styles = true
348
+ end
349
+
350
+ parser.on("--inline-css [STYLESHEET]", "Write every stylesheet the template renders into `style` attributes, reading one from a file first if given (for compile/render commands) (default: false)") do |stylesheet|
351
+ self.inline_css = Array(inline_css) + Array(stylesheet)
352
+ end
353
+
354
+ parser.on("--slots [MODE]", "Emit slot markers for reactive rendering, server (default) or client (for compile/render commands)") do |mode|
355
+ self.slots = (mode || "server").to_sym
356
+
357
+ unless Herb::Engine::Slots::Visitor::MODES.include?(slots)
358
+ puts "Unknown --slots mode: #{mode}"
359
+ puts "Expected one of: #{Herb::Engine::Slots::Visitor::MODES.join(", ")}"
360
+
361
+ exit(1)
362
+ end
363
+ end
364
+
332
365
  parser.on("--tool TOOL", "Show config for specific tool: linter, formatter (for config command)") do |t|
333
366
  self.tool = t.to_sym
334
367
  end
@@ -388,7 +421,7 @@ class Herb::CLI
388
421
 
389
422
  case subcommand
390
423
  when "check"
391
- require_relative "action_view/render_analyzer"
424
+ require_relative "analysis/render_analyzer"
392
425
 
393
426
  path = File.expand_path(@file || ".")
394
427
 
@@ -397,12 +430,12 @@ class Herb::CLI
397
430
  exit(1)
398
431
  end
399
432
 
400
- analyzer = Herb::ActionView::RenderAnalyzer.new(path)
433
+ analyzer = Herb::Analysis::RenderAnalyzer.new(path)
401
434
  has_issues = analyzer.check!
402
435
 
403
436
  exit(has_issues ? 1 : 0)
404
437
  when "graph"
405
- require_relative "action_view/render_analyzer"
438
+ require_relative "analysis/render_analyzer"
406
439
 
407
440
  path = @file || "."
408
441
 
@@ -413,7 +446,7 @@ class Herb::CLI
413
446
 
414
447
  path = File.expand_path(path)
415
448
  project_root = File.directory?(path) ? path : config.project_root&.to_s || File.dirname(path)
416
- analyzer = Herb::ActionView::RenderAnalyzer.new(project_root)
449
+ analyzer = Herb::Analysis::RenderAnalyzer.new(project_root)
417
450
 
418
451
  if File.file?(path)
419
452
  analyzer.graph_file!(path)
@@ -421,6 +454,104 @@ class Herb::CLI
421
454
  analyzer.graph!
422
455
  end
423
456
 
457
+ exit(0)
458
+ when "dependencies"
459
+ require_relative "analysis/template_dependencies"
460
+
461
+ path = @file || "."
462
+ path = File.expand_path(path)
463
+
464
+ if File.file?(path)
465
+ project_root = config.project_root&.to_s || File.dirname(path)
466
+ dep_analyzer = Herb::Analysis::TemplateDependencies.new(project_root)
467
+ dep_analyzer.scan_helpers!
468
+ result = dep_analyzer.analyze(path)
469
+ relative = Pathname.new(path).relative_path_from(project_root).to_s
470
+ is_entry_point = !File.basename(path).start_with?("_")
471
+
472
+ puts ""
473
+ puts " #{bold("Herb")} \u{1f33f} #{dimmed("v#{Herb::VERSION}")}"
474
+ puts ""
475
+ puts " #{cyan(relative)} #{dimmed(is_entry_point ? "(entry point)" : "(partial)")}"
476
+ puts ""
477
+
478
+ print_dependency_result(result)
479
+
480
+ if is_entry_point && result.instance_variables.any?
481
+ puts " #{bold("State flow")} #{dimmed("(which templates are affected by each state change)")}"
482
+ puts ""
483
+
484
+ result.instance_variables.each do |ivar|
485
+ affected = dep_analyzer.affected_templates(path, ivar)
486
+ short = affected.map { |f| Pathname.new(f).relative_path_from(project_root).to_s }
487
+
488
+ puts " #{yellow(ivar)} #{dimmed("(#{short.size} #{short.size == 1 ? "template" : "templates"})")}"
489
+ short.each { |f| puts " #{dimmed(f)}" }
490
+ puts ""
491
+ end
492
+
493
+ index = dep_analyzer.dependency_index(path)
494
+
495
+ if index.any?
496
+ puts " #{bold("Node index")} #{dimmed("(which DOM nodes are affected by each state change)")}"
497
+ puts ""
498
+
499
+ index.each do |state, nodes|
500
+ puts " #{yellow(state)} #{dimmed("(#{nodes.size} #{nodes.size == 1 ? "node" : "nodes"})")}"
501
+
502
+ nodes.each_with_index do |n, i|
503
+ connector = i == nodes.size - 1 ? "\u2514\u2500\u2500" : "\u251c\u2500\u2500"
504
+ attr = n[:attribute] ? " #{dimmed("attr=#{n[:attribute]}")}" : ""
505
+ puts " #{connector} #{dimmed("[#{n[:node_path].join(",")}]")} #{n[:type]}#{attr} #{dimmed(one_line(n[:expression], 60))}"
506
+ end
507
+
508
+ puts ""
509
+ end
510
+ end
511
+ end
512
+ elsif File.directory?(path)
513
+ require_relative "analysis/render_analyzer"
514
+
515
+ dep_analyzer = Herb::Analysis::TemplateDependencies.new(path)
516
+ dep_analyzer.scan_helpers!
517
+
518
+ render_analyzer = Herb::Analysis::RenderAnalyzer.new(path)
519
+ erb_files = render_analyzer.send(:find_erb_files)
520
+
521
+ puts ""
522
+ puts " #{bold("Herb")} \u{1f33f} #{dimmed("v#{Herb::VERSION}")}"
523
+ puts ""
524
+ puts dimmed(" Analyzing dependencies in #{erb_files.size} files...")
525
+ puts ""
526
+
527
+ erb_files.each do |file|
528
+ result = dep_analyzer.analyze(file)
529
+ relative = Pathname.new(file).relative_path_from(path).to_s
530
+
531
+ next if result.instance_variables.empty? && result.constants.empty? && result.unknown_calls.empty?
532
+
533
+ puts " #{cyan(relative)}"
534
+ puts " #{dimmed("state:")} #{result.instance_variables.join(", ")}" if result.instance_variables.any?
535
+ puts " #{dimmed("constants:")} #{result.constants.join(", ")}" if result.constants.any?
536
+ puts " #{dimmed("locals declared:")} #{result.locals_declared.join(", ")}" if result.locals_declared.any?
537
+ puts " #{dimmed("locals received:")} #{result.locals_received.keys.join(", ")}" if result.locals_received.any?
538
+ puts " #{dimmed("unknown:")} #{result.unknown_calls.join(", ")}" if result.unknown_calls.any?
539
+ puts ""
540
+ end
541
+ else
542
+ puts "Not a file or directory: '#{path}'."
543
+ exit(1)
544
+ end
545
+
546
+ exit(0)
547
+ when "flow"
548
+ actionview_flow(config)
549
+ exit(0)
550
+ when "context"
551
+ actionview_context(config)
552
+ exit(0)
553
+ when "signature"
554
+ actionview_signature(config)
424
555
  exit(0)
425
556
  when "render"
426
557
  @file = @args[2]
@@ -433,14 +564,23 @@ class Herb::CLI
433
564
  bundle exec herb actionview [subcommand] [options]
434
565
 
435
566
  Subcommands:
436
- check [path] Check if render calls resolve to valid partial files
437
- graph [path] Show render dependency graph for a project or file
438
- render [file] Render ERB template using ActionView helpers
567
+ check [path] Check render calls and flag dependency warnings
568
+ graph [path] Show render dependency graph for a project or file
569
+ dependencies [path] Show template dependency manifest (state, locals, helpers)
570
+ flow <file> <state> Trace one piece of state through every partial it reaches
571
+ context <partial> Show what a partial is always, sometimes, or never rendered inside
572
+ signature <partial> Show the strict locals a partial receives across every call site
573
+ render [file] Render ERB template using ActionView helpers
439
574
 
440
575
  Examples:
441
576
  bundle exec herb actionview check
442
577
  bundle exec herb actionview graph
443
578
  bundle exec herb actionview graph app/views/posts/show.html.erb
579
+ bundle exec herb actionview dependencies app/views/posts/show.html.erb
580
+ bundle exec herb actionview dependencies
581
+ bundle exec herb actionview flow app/views/posts/show.html.erb @post
582
+ bundle exec herb actionview context app/views/posts/_card.html.erb
583
+ bundle exec herb actionview signature app/views/posts/_card.html.erb
444
584
  bundle exec herb actionview render app/views/posts/show.html.erb
445
585
 
446
586
  HELP
@@ -452,6 +592,325 @@ class Herb::CLI
452
592
  end
453
593
  end
454
594
 
595
+ def print_dependency_result(result)
596
+ if result.instance_variables.any?
597
+ puts " #{bold("Instance variables")} #{dimmed("(state)")}"
598
+ result.instance_variables.each { |v| puts " #{v}" }
599
+ puts ""
600
+ end
601
+
602
+ if result.constants.any?
603
+ puts " #{bold("Constants")}"
604
+ result.constants.each { |c| puts " #{c}" }
605
+ puts ""
606
+ end
607
+
608
+ if result.locals_declared.any?
609
+ puts " #{bold("Locals declared")} #{dimmed("(strict locals)")}"
610
+ result.locals_declared.each { |l| puts " #{l}" }
611
+ puts ""
612
+ end
613
+
614
+ if result.locals_received.any?
615
+ puts " #{bold("Locals received")} #{dimmed("(from render calls)")}"
616
+ result.locals_received.each { |name, value| puts " #{name} #{dimmed("\u2190")} #{value}" }
617
+ puts ""
618
+ end
619
+
620
+ if result.helper_calls.any?
621
+ puts " #{bold("Helper calls")} #{dimmed("(known)")}"
622
+ result.helper_calls.each { |h| puts " #{dimmed(h)}" }
623
+ puts ""
624
+ end
625
+
626
+ return unless result.unknown_calls.any?
627
+
628
+ puts " #{bold("Unknown calls")}"
629
+ result.unknown_calls.each { |u| puts " #{yellow(u)}" }
630
+ puts ""
631
+ end
632
+
633
+ def actionview_project_index(config)
634
+ require_relative "analysis/project_index"
635
+
636
+ unless @file
637
+ puts "Please provide a partial path."
638
+ exit(1)
639
+ end
640
+
641
+ path = File.expand_path(@file)
642
+
643
+ unless File.file?(path)
644
+ puts "Not a file: '#{path}'."
645
+ exit(1)
646
+ end
647
+
648
+ project_root = config.project_root&.to_s || File.dirname(path)
649
+ project = Herb::Analysis::ProjectIndex.new(project_root)
650
+ project.index_all
651
+
652
+ [project, path, project_root]
653
+ end
654
+
655
+ def actionview_header(path, project_root)
656
+ relative = Pathname.new(path).relative_path_from(Pathname.new(project_root)).to_s
657
+
658
+ puts ""
659
+ puts " #{bold("Herb")} \u{1f33f} #{dimmed("v#{Herb::VERSION}")}"
660
+ puts ""
661
+ puts " #{cyan(relative)}"
662
+ puts ""
663
+ end
664
+
665
+ def verdict_marker(verdict)
666
+ case verdict
667
+ when :always then green("\u2713")
668
+ when :never then dimmed("\u2717")
669
+ when :mixed then yellow("~")
670
+ else dimmed("?")
671
+ end
672
+ end
673
+
674
+ def actionview_flow(config)
675
+ require_relative "analysis/template_dependencies"
676
+
677
+ @file = @args[2]
678
+ state = @args[3]
679
+
680
+ unless @file
681
+ puts "Please provide a template path."
682
+ puts "Example: herb actionview flow app/views/posts/show.html.erb @post"
683
+ exit(1)
684
+ end
685
+
686
+ path = File.expand_path(@file)
687
+
688
+ unless File.file?(path)
689
+ puts "Not a file: '#{path}'."
690
+ exit(1)
691
+ end
692
+
693
+ project_root = config.project_root&.to_s || File.dirname(path)
694
+ dependencies = Herb::Analysis::TemplateDependencies.new(project_root)
695
+ dependencies.scan_helpers!
696
+
697
+ result = dependencies.analyze(path)
698
+ available = (result.instance_variables + result.constants).sort
699
+
700
+ unless state
701
+ actionview_header(path, project_root)
702
+ print_available_state(dependencies, path, available, project_root)
703
+ exit(0)
704
+ end
705
+
706
+ flow = dependencies.state_flow(path, state)
707
+
708
+ actionview_header(path, project_root)
709
+
710
+ unless flow
711
+ puts " #{dimmed("'#{state}' is not read by this template.")}"
712
+ puts ""
713
+
714
+ print_available_state(dependencies, path, available, project_root)
715
+
716
+ exit(1)
717
+ end
718
+
719
+ puts " #{bold("State flow")} #{dimmed("for #{yellow(state)}")}"
720
+ puts ""
721
+
722
+ print_flow_node(flow, project_root, "", true, true)
723
+
724
+ puts ""
725
+ end
726
+
727
+ def print_available_state(dependencies, path, available, project_root)
728
+ if available.empty?
729
+ puts " #{dimmed("This template does not read any instance variables or constants.")}"
730
+ puts ""
731
+
732
+ return
733
+ end
734
+
735
+ relative = Pathname.new(path).relative_path_from(Pathname.new(project_root)).to_s
736
+
737
+ puts " #{bold("Available state")} #{dimmed("(#{available.size} in this template)")}"
738
+ puts ""
739
+
740
+ available.each do |name|
741
+ affected = dependencies.affected_templates(path, name)
742
+ reach = affected.size <= 1 ? dimmed("this template only") : dimmed("#{affected.size} templates")
743
+
744
+ puts " #{yellow(name)} #{reach}"
745
+ end
746
+
747
+ puts ""
748
+ puts " #{dimmed("Trace one with:")}"
749
+ puts " #{dimmed("herb actionview flow #{relative} #{available.first}")}"
750
+ puts ""
751
+ end
752
+
753
+ def nesting_depths(nodes)
754
+ stack = [] #: Array[Array[Integer]]
755
+
756
+ nodes.map do |node|
757
+ path = node[:node_path] || []
758
+
759
+ stack.pop while stack.any? && !path_contains?(stack.last, path)
760
+
761
+ depth = stack.size
762
+ stack.push(path)
763
+
764
+ depth
765
+ end
766
+ end
767
+
768
+ def path_contains?(outer, inner)
769
+ outer.length < inner.length && inner[0, outer.length] == outer
770
+ end
771
+
772
+ def one_line(expression, limit = 72)
773
+ text = expression.to_s.gsub(/\s+/, " ").strip
774
+
775
+ text.length > limit ? "#{text[0, limit - 1]}\u2026" : text
776
+ end
777
+
778
+ def print_flow_node(node, project_root, prefix, last, root)
779
+ puts " #{prefix}\u2502" unless root
780
+
781
+ relative = Pathname.new(node.file).relative_path_from(Pathname.new(project_root)).to_s
782
+ connector = if root
783
+ ""
784
+ else
785
+ last ? "\u2514\u2500\u2500 " : "\u251c\u2500\u2500 "
786
+ end
787
+ carried = node.names.map { |name| yellow(name) }.join(", ")
788
+
789
+ via = if node.via&.any?
790
+ dimmed(" \u2190 #{node.via.map { |name, expression| "#{name}: #{expression}" }.join(", ")}")
791
+ else
792
+ ""
793
+ end
794
+
795
+ puts " #{prefix}#{connector}#{cyan(relative)} #{dimmed("[")}#{carried}#{dimmed("]")}#{via}"
796
+
797
+ child_prefix = if root
798
+ prefix
799
+ else
800
+ prefix + (last ? " " : "\u2502 ")
801
+ end
802
+
803
+ depths = nesting_depths(node.nodes)
804
+
805
+ node.nodes.each_with_index do |affected, index|
806
+ nesting = " " * depths[index]
807
+
808
+ puts " #{child_prefix}#{nesting}#{dimmed("\u00b7")} #{affected[:type]} #{dimmed(one_line(affected[:expression]))} #{dimmed("(#{affected[:location]})")}"
809
+ end
810
+
811
+ node.children.each_with_index do |child, index|
812
+ print_flow_node(child, project_root, child_prefix, index == node.children.size - 1, false)
813
+ end
814
+ end
815
+
816
+ def actionview_context(config)
817
+ @file = @args[2]
818
+ project, path, project_root = actionview_project_index(config)
819
+
820
+ actionview_header(path, project_root)
821
+
822
+ context = project.graph.context_of(path)
823
+ callers = project.graph.callers_of(path)
824
+
825
+ if callers.empty?
826
+ puts " #{dimmed("Not rendered by any template.")}"
827
+ puts ""
828
+ exit(0)
829
+ end
830
+
831
+ puts " #{bold("Rendered inside")} #{dimmed("(#{context.chains.size} #{context.chains.size == 1 ? "path" : "paths"})")}"
832
+ puts ""
833
+
834
+ context.chains.each_with_index do |chain, index|
835
+ connector = index == context.chains.size - 1 ? "\u2514\u2500\u2500" : "\u251c\u2500\u2500"
836
+ trail = chain.tags.any? ? chain.tags.join(dimmed(" \u203a ")) : dimmed("(top level)")
837
+ times = chain.occurrences > 1 ? dimmed(" \u00d7#{chain.occurrences}") : ""
838
+
839
+ puts " #{connector} #{trail}#{times}"
840
+ end
841
+
842
+ puts ""
843
+
844
+ unless context.resolved
845
+ puts " #{yellow("Some call sites could not be resolved, so 'never' is reported as unknown.")}"
846
+ puts ""
847
+ end
848
+
849
+ tags = context.chains.flat_map(&:tags).uniq.sort
850
+
851
+ return if tags.empty?
852
+
853
+ puts " #{bold("Verdicts")}"
854
+ puts ""
855
+
856
+ tags.each do |tag|
857
+ verdict = context.ancestor_verdict([], tag)
858
+
859
+ puts " #{verdict_marker(verdict)} #{tag} #{dimmed(verdict.to_s)}"
860
+ end
861
+
862
+ puts ""
863
+ end
864
+
865
+ def actionview_signature(config)
866
+ @file = @args[2]
867
+ project, path, project_root = actionview_project_index(config)
868
+
869
+ actionview_header(path, project_root)
870
+
871
+ signature = project.graph.infer_signature(path)
872
+ declaration = project.partials.declaration_for_file(path)
873
+
874
+ if signature.call_site_count.zero?
875
+ puts " #{dimmed("Not rendered by any template, so there is nothing to infer.")}"
876
+ puts ""
877
+ exit(0)
878
+ end
879
+
880
+ puts " #{bold("Inferred")} #{dimmed("(from #{signature.call_site_count} #{signature.call_site_count == 1 ? "call site" : "call sites"})")}"
881
+ puts ""
882
+ puts " #{signature.strict_locals_declaration}"
883
+ puts ""
884
+
885
+ if declaration&.has_declaration
886
+ puts " #{bold("Declared")}"
887
+ puts ""
888
+ puts " #{dimmed("required")} #{declaration.required_locals.join(", ")}" if declaration.required_locals.any?
889
+ puts " #{dimmed("optional")} #{declaration.optional_locals.join(", ")}" if declaration.optional_locals.any?
890
+ puts " #{dimmed("keyword rest")} #{declaration.has_keyword_rest}"
891
+ puts ""
892
+
893
+ declared = declaration.required_locals + declaration.optional_locals
894
+ passed = signature.locals.map(&:name)
895
+
896
+ undeclared = passed - declared
897
+ unused = declared - passed
898
+
899
+ if undeclared.any? && !declaration.has_keyword_rest
900
+ puts " #{yellow("Passed but not declared:")} #{undeclared.join(", ")}"
901
+ puts ""
902
+ end
903
+
904
+ if unused.any?
905
+ puts " #{dimmed("Declared but never passed:")} #{unused.join(", ")}"
906
+ puts ""
907
+ end
908
+ else
909
+ puts " #{dimmed("This partial does not declare strict locals yet.")}"
910
+ puts ""
911
+ end
912
+ end
913
+
455
914
  def actionview_render
456
915
  require "action_view"
457
916
 
@@ -666,25 +1125,59 @@ class Herb::CLI
666
1125
  exit(0)
667
1126
  end
668
1127
 
1128
+ def scoped_style_visitor
1129
+ require_relative "engine/scoped_style/visitor"
1130
+ Herb.ensure_installed("lightningcss")
1131
+
1132
+ Herb::Engine::ScopedStyle::Visitor.new
1133
+ end
1134
+
1135
+ def css_inliner_visitor
1136
+ require_relative "engine/css_inliner/visitor"
1137
+ Herb.ensure_installed("css_inline")
1138
+
1139
+ Herb::Engine::CSSInliner::Visitor.new(stylesheets: inline_css)
1140
+ end
1141
+
1142
+ def optimize_visitor
1143
+ require_relative "engine/visitors/optimize_visitor"
1144
+
1145
+ Herb::Engine::OptimizeVisitor.new
1146
+ end
1147
+
669
1148
  def compile_template
670
1149
  require_relative "engine"
671
1150
 
672
1151
  begin
1152
+ source = file_content
673
1153
  options = {}
1154
+
1155
+ slot_mode = slots || Herb::Engine::Slots::Visitor.directive_mode(source)
1156
+ slot_visitor = Herb::Engine::Slots::Visitor.new(mode: slot_mode) if slot_mode
1157
+ visitors = []
1158
+
1159
+ visitors << slot_visitor if slot_visitor
1160
+ visitors << scoped_style_visitor if scoped_styles
1161
+ visitors << css_inliner_visitor if inline_css
1162
+ visitors << optimize_visitor if optimize
1163
+
674
1164
  options[:filename] = @file if @file
675
1165
  options[:escape] = no_escape ? false : true
676
1166
  options[:freeze] = true if freeze
677
- options[:strict] = strict.nil? || strict
1167
+ options[:parser_options] = (options[:parser_options] || {}).merge(strict: strict.nil? || strict)
678
1168
 
679
1169
  if debug
680
1170
  options[:debug] = true
681
1171
  options[:debug_filename] = @file if @file
682
1172
  end
683
1173
 
684
- options[:optimize] = true if optimize
685
- options[:trim] = true if trim
1174
+ options[:trim] = false if no_trim
686
1175
  options[:validate_ruby] = true
687
- engine = Herb::Engine.new(file_content, options)
1176
+ options[:visitors] = visitors unless visitors.empty?
1177
+
1178
+ engine = Herb::Engine.new(source, options)
1179
+
1180
+ print_warnings(slot_visitor&.warnings || []) unless json
688
1181
 
689
1182
  if json
690
1183
  result = {
@@ -692,7 +1185,7 @@ class Herb::CLI
692
1185
  source: engine.src,
693
1186
  filename: engine.filename,
694
1187
  bufvar: engine.bufvar,
695
- strict: options[:strict],
1188
+ strict: options[:parser_options][:strict],
696
1189
  }
697
1190
 
698
1191
  puts result.to_json
@@ -717,7 +1210,7 @@ class Herb::CLI
717
1210
  else
718
1211
  puts e.compiled_source if e.compiled_source
719
1212
  puts
720
- puts e.message
1213
+ puts e.formatted_errors(highlight: Herb::Colors.enabled?) || e.message
721
1214
  end
722
1215
 
723
1216
  exit(1)
@@ -732,7 +1225,7 @@ class Herb::CLI
732
1225
  elsif silent
733
1226
  puts "Failed"
734
1227
  else
735
- puts e.message
1228
+ puts e.formatted_errors(highlight: Herb::Colors.enabled?) || e.message
736
1229
  end
737
1230
 
738
1231
  exit(1)
@@ -755,37 +1248,67 @@ class Herb::CLI
755
1248
  end
756
1249
  end
757
1250
 
1251
+ def print_warnings(warnings)
1252
+ return if warnings.empty?
1253
+
1254
+ $stderr.puts
1255
+
1256
+ warnings.each do |warning|
1257
+ location = [@file, warning.location&.start&.line, warning.location&.start&.column].compact.join(":")
1258
+
1259
+ warn "#{bold(yellow("warning"))}: #{warning.message}"
1260
+ warn " #{dimmed(location)}"
1261
+ warn " #{dimmed(warning.expression.to_s)}" if warning.respond_to?(:expression) && warning.expression
1262
+ $stderr.puts
1263
+ end
1264
+ end
1265
+
758
1266
  def render_template
759
1267
  require_relative "engine"
760
1268
 
761
1269
  begin
1270
+ source = file_content
762
1271
  options = {}
1272
+
1273
+ slot_mode = slots || Herb::Engine::Slots::Visitor.directive_mode(source)
1274
+ slot_visitor = Herb::Engine::Slots::Visitor.new(mode: slot_mode) if slot_mode
1275
+
763
1276
  options[:filename] = @file if @file
764
1277
  options[:escape] = no_escape ? false : true
765
1278
  options[:freeze] = true if freeze
766
- options[:strict] = strict.nil? || strict
1279
+ options[:parser_options] = (options[:parser_options] || {}).merge(strict: strict.nil? || strict)
767
1280
 
768
1281
  if debug
769
1282
  options[:debug] = true
770
1283
  options[:debug_filename] = @file if @file
771
1284
  end
772
1285
 
773
- options[:optimize] = true if optimize
774
- options[:trim] = true if trim
1286
+ options[:trim] = false if no_trim
775
1287
 
776
- engine = Herb::Engine.new(file_content, options)
1288
+ visitors = []
1289
+ visitors << slot_visitor if slot_visitor
1290
+ visitors << scoped_style_visitor if scoped_styles
1291
+ visitors << css_inliner_visitor if inline_css
1292
+ visitors << optimize_visitor if optimize
1293
+ options[:visitors] = visitors unless visitors.empty?
1294
+
1295
+ engine = Herb::Engine.new(source, options)
777
1296
  compiled_code = engine.src
778
1297
 
779
1298
  rendered_output = eval(compiled_code)
780
1299
 
1300
+ print_warnings(slot_visitor&.warnings || []) unless json
1301
+
781
1302
  if json
782
1303
  result = {
783
1304
  success: true,
784
1305
  output: rendered_output,
785
1306
  filename: engine.filename,
786
- strict: options[:strict],
1307
+ strict: options[:parser_options][:strict],
787
1308
  }
788
1309
 
1310
+ result[:slots] = slot_visitor.schema if slot_visitor
1311
+
789
1312
  puts result.to_json
790
1313
  elsif silent
791
1314
  puts "Success"
@@ -805,7 +1328,7 @@ class Herb::CLI
805
1328
  elsif silent
806
1329
  puts "Failed"
807
1330
  else
808
- puts e.message
1331
+ puts e.formatted_errors(highlight: Herb::Colors.enabled?) || e.message
809
1332
  end
810
1333
 
811
1334
  exit(1)