herb 0.10.4 → 0.11.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.
Files changed (784) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +16 -6
  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 +1240 -790
  284. data/exe/herb +2 -0
  285. data/ext/herb/error_helpers.c +355 -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 -89
  289. data/ext/herb/extension_helpers.c +81 -42
  290. data/ext/herb/extension_helpers.h +4 -1
  291. data/ext/herb/nodes.c +826 -358
  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 +2 -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 +1342 -105
  327. data/lib/herb/backend.rb +73 -0
  328. data/lib/herb/bootstrap.rb +3 -7
  329. data/lib/herb/cli.rb +550 -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 +175 -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 +346 -108
  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 +854 -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 +128 -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 +1326 -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 +2256 -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} +121 -93
  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 -257
  422. data/lib/herb/ensure_installed.rb +43 -0
  423. data/lib/herb/errors.rb +646 -157
  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 +65 -46
  432. data/lib/herb/position.rb +19 -0
  433. data/lib/herb/prism_inspect.rb +1 -4
  434. data/lib/herb/project.rb +28 -15
  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 +662 -89
  483. data/sig/herb/backend.rbs +52 -0
  484. data/sig/herb/colors.rbs +3 -0
  485. data/sig/herb/configuration.rbs +27 -3
  486. data/sig/herb/dev/assets.rbs +27 -0
  487. data/sig/herb/dev/classifier.rbs +57 -0
  488. data/sig/herb/dev/embedded.rbs +21 -0
  489. data/sig/herb/dev/pipeline.rbs +118 -0
  490. data/sig/herb/dev/protocol.rbs +78 -0
  491. data/sig/herb/dev/runner.rbs +61 -23
  492. data/sig/herb/dev/server.rbs +49 -22
  493. data/sig/herb/dev/server_entry.rbs +46 -21
  494. data/sig/herb/dev/watcher.rbs +84 -0
  495. data/sig/herb/dev.rbs +58 -0
  496. data/sig/herb/diagnostic/formatter.rbs +28 -0
  497. data/sig/herb/diagnostic.rbs +88 -0
  498. data/sig/herb/diff/operation.rbs +36 -0
  499. data/sig/herb/diff/result.rbs +36 -0
  500. data/sig/herb/diff.rbs +6 -0
  501. data/sig/herb/engine/collector.rbs +48 -0
  502. data/sig/herb/engine/compiler.rbs +62 -23
  503. data/sig/herb/engine/component_tags/constant_resolver.rbs +19 -0
  504. data/sig/herb/engine/component_tags/partial_resolver.rbs +30 -0
  505. data/sig/herb/engine/component_tags/resolver.rbs +25 -0
  506. data/sig/herb/engine/component_tags/visitor.rbs +69 -0
  507. data/sig/herb/engine/css_inliner/inliner.rbs +65 -0
  508. data/sig/herb/engine/css_inliner/visitor.rbs +87 -0
  509. data/sig/herb/engine/errors.rbs +68 -0
  510. data/sig/herb/engine/helpers.rbs +28 -0
  511. data/sig/herb/engine/inline_render/inliner.rbs +39 -0
  512. data/sig/herb/engine/inline_render/visitor.rbs +104 -0
  513. data/sig/herb/engine/runtime/entry.rbs +43 -0
  514. data/sig/herb/engine/runtime/error_page.rbs +115 -0
  515. data/sig/herb/engine/runtime/html_safe_assertions.rbs +99 -0
  516. data/sig/herb/engine/runtime/journal/summary.rbs +172 -0
  517. data/sig/herb/engine/runtime/journal.rbs +126 -0
  518. data/sig/herb/engine/runtime/middleware.rbs +96 -0
  519. data/sig/herb/engine/runtime/observations.rbs +38 -0
  520. data/sig/herb/engine/runtime/report.rbs +97 -0
  521. data/sig/herb/engine/runtime/session.rbs +233 -0
  522. data/sig/herb/engine/runtime.rbs +14 -0
  523. data/sig/herb/engine/scoped_style/channel.rbs +45 -0
  524. data/sig/herb/engine/scoped_style/collector.rbs +45 -0
  525. data/sig/herb/engine/scoped_style/visitor.rbs +220 -0
  526. data/sig/herb/engine/slots/annotation.rbs +72 -0
  527. data/sig/herb/engine/slots/bindings.rbs +34 -0
  528. data/sig/herb/engine/slots/callee.rbs +62 -0
  529. data/sig/herb/engine/slots/component_definitions.rbs +19 -0
  530. data/sig/herb/engine/slots/components/async.rbs +20 -0
  531. data/sig/herb/engine/slots/components/base.rbs +60 -0
  532. data/sig/herb/engine/slots/components/deferred.rbs +29 -0
  533. data/sig/herb/engine/slots/components/fallback.rbs +21 -0
  534. data/sig/herb/engine/slots/components/fragment.rbs +34 -0
  535. data/sig/herb/engine/slots/components/lazy.rbs +20 -0
  536. data/sig/herb/engine/slots/components.rbs +30 -0
  537. data/sig/herb/engine/slots/dependencies/channel.rbs +51 -0
  538. data/sig/herb/engine/slots/dependencies.rbs +94 -0
  539. data/sig/herb/engine/slots/dynamics_compiler.rbs +349 -0
  540. data/sig/herb/engine/slots/identifier.rbs +19 -0
  541. data/sig/herb/engine/slots/manifest/channel.rbs +49 -0
  542. data/sig/herb/engine/slots/manifest/collector.rbs +45 -0
  543. data/sig/herb/engine/slots/markers.rbs +85 -0
  544. data/sig/herb/engine/slots/render_bindings.rbs +69 -0
  545. data/sig/herb/engine/slots/schema_compiler.rbs +62 -0
  546. data/sig/herb/engine/slots/state_anchor.rbs +41 -0
  547. data/sig/herb/engine/slots/state_compiler.rbs +258 -0
  548. data/sig/herb/engine/slots/state_directives.rbs +278 -0
  549. data/sig/herb/engine/slots/state_kinds.rbs +24 -0
  550. data/sig/herb/engine/slots/state_operators.rbs +19 -0
  551. data/sig/herb/engine/slots/state_overrides.rbs +33 -0
  552. data/sig/herb/engine/slots/state_predicates.rbs +16 -0
  553. data/sig/herb/engine/slots/state_transforms.rbs +15 -0
  554. data/sig/herb/engine/slots/statics.rbs +67 -0
  555. data/sig/herb/engine/slots/subtree.rbs +49 -0
  556. data/sig/herb/engine/slots/subtree_compiler.rbs +130 -0
  557. data/sig/herb/engine/slots/types.rbs +46 -0
  558. data/sig/herb/engine/slots/visitor.rbs +599 -0
  559. data/sig/herb/engine/validators/accessibility_validator.rbs +1 -1
  560. data/sig/herb/engine/validators/base.rbs +28 -0
  561. data/sig/herb/engine/validators/generator_template_validator.rbs +19 -0
  562. data/sig/herb/engine/validators/nesting_validator.rbs +1 -1
  563. data/sig/herb/engine/validators/render_validator.rbs +2 -6
  564. data/sig/herb/engine/validators/security_validator.rbs +1 -3
  565. data/sig/herb/engine/validators.rbs +21 -0
  566. data/sig/herb/engine/visitors/auto_close_omitted_tags_visitor.rbs +26 -0
  567. data/sig/herb/engine/visitors/content_for_visitor.rbs +52 -0
  568. data/sig/herb/engine/{debug_visitor.rbs → visitors/debug_visitor.rbs} +26 -15
  569. data/sig/herb/engine/visitors/html_safe_assertions_visitor.rbs +100 -0
  570. data/sig/herb/engine/visitors/instrumentation_visitor.rbs +111 -0
  571. data/sig/herb/engine/visitors/optimize_visitor.rbs +246 -0
  572. data/sig/herb/engine/visitors/remove_comments_visitor.rbs +66 -0
  573. data/sig/herb/engine/visitors/source_attribution_visitor.rbs +90 -0
  574. data/sig/herb/engine.rbs +47 -41
  575. data/sig/herb/ensure_installed.rbs +9 -0
  576. data/sig/herb/errors.rbs +293 -0
  577. data/sig/herb/fingerprint.rbs +37 -0
  578. data/sig/herb/html/elements.rbs +19 -0
  579. data/sig/herb/html/util.rbs +15 -1
  580. data/sig/herb/lex_result.rbs +0 -6
  581. data/sig/herb/locate.rbs +89 -0
  582. data/sig/herb/location.rbs +12 -0
  583. data/sig/herb/parse_result.rbs +10 -8
  584. data/sig/herb/parser_options.rbs +47 -21
  585. data/sig/herb/position.rbs +11 -0
  586. data/sig/herb/range.rbs +3 -0
  587. data/sig/herb/ruby_program.rbs +51 -0
  588. data/sig/herb/source_path.rbs +132 -0
  589. data/sig/herb/token.rbs +5 -2
  590. data/sig/herb/visitor/context/origin.rbs +51 -0
  591. data/sig/herb/visitor/context/replacements.rbs +39 -0
  592. data/sig/herb/visitor/context.rbs +83 -0
  593. data/sig/herb/visitor/context_aware.rbs +32 -0
  594. data/sig/herb/visitor/diagnostics.rbs +82 -0
  595. data/sig/herb/visitor/experimental.rbs +23 -0
  596. data/sig/herb/visitor/parser_option_requirements.rbs +76 -0
  597. data/sig/herb/visitor/stack.rbs +91 -0
  598. data/sig/herb/visitor.rbs +22 -0
  599. data/sig/herb/warnings.rbs +10 -0
  600. data/sig/herb.rbs +27 -10
  601. data/sig/serialized_ast_errors.rbs +58 -1
  602. data/sig/serialized_ast_nodes.rbs +58 -2
  603. data/src/analyze/action_view/attribute_extraction_helpers.c +73 -36
  604. data/src/analyze/action_view/generated_handlers.c +37 -1
  605. data/src/analyze/action_view/generated_handlers.h +2 -1
  606. data/src/analyze/action_view/helper_registry.c +1353 -58
  607. data/src/analyze/action_view/registry.c +10 -17
  608. data/src/analyze/action_view/stylesheet_link_tag.c +40 -0
  609. data/src/analyze/action_view/tag_helper_node_builders.c +57 -0
  610. data/src/analyze/action_view/tag_helpers.c +423 -93
  611. data/src/analyze/analyze.c +301 -64
  612. data/src/analyze/analyze_helpers.c +420 -23
  613. data/src/analyze/builders.c +79 -51
  614. data/src/analyze/conditional_elements.c +13 -8
  615. data/src/analyze/conditional_open_tags.c +11 -5
  616. data/src/analyze/control_type.c +16 -1
  617. data/src/analyze/herb_directives.c +701 -0
  618. data/src/analyze/invalid_structures.c +11 -7
  619. data/src/analyze/iteration_nodes.c +306 -0
  620. data/src/analyze/missing_end.c +116 -32
  621. data/src/analyze/parse_errors.c +8 -3
  622. data/src/analyze/postfix_conditionals.c +98 -63
  623. data/src/analyze/prism_annotate.c +13 -4
  624. data/src/analyze/render_nodes.c +345 -123
  625. data/src/analyze/strict_locals.c +30 -26
  626. data/src/analyze/ternary_conditionals.c +95 -70
  627. data/src/analyze/transform.c +43 -1
  628. data/src/ast/ast_node.c +3 -13
  629. data/src/ast/ast_nodes.c +911 -112
  630. data/src/ast/ast_pretty_print.c +161 -2
  631. data/src/diff/herb_diff.c +8 -0
  632. data/src/diff/herb_diff_attributes.c +1 -0
  633. data/src/diff/herb_diff_children.c +4 -3
  634. data/src/diff/herb_diff_helpers.c +7 -1
  635. data/src/diff/herb_diff_nodes.c +186 -71
  636. data/src/diff/herb_hash_tree.c +99 -1
  637. data/src/errors.c +1096 -128
  638. data/src/extract.c +30 -8
  639. data/src/herb.c +30 -11
  640. data/src/include/analyze/action_view/attribute_extraction_helpers.h +1 -1
  641. data/src/include/analyze/action_view/helper_registry.h +28 -4
  642. data/src/include/analyze/action_view/tag_helper_handler.h +1 -1
  643. data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
  644. data/src/include/analyze/analyze.h +11 -0
  645. data/src/include/analyze/builders.h +16 -0
  646. data/src/include/analyze/helpers.h +23 -2
  647. data/src/include/analyze/herb_directive_kinds.h +41 -0
  648. data/src/include/analyze/herb_directives.h +14 -0
  649. data/src/include/analyze/iteration_nodes.h +11 -0
  650. data/src/include/analyze/prism_annotate.h +3 -3
  651. data/src/include/analyze/ternary_conditionals.h +2 -0
  652. data/src/include/ast/ast_node.h +1 -5
  653. data/src/include/ast/ast_nodes.h +76 -2
  654. data/src/include/ast/ast_pretty_print.h +1 -1
  655. data/src/include/diff/herb_diff.h +11 -0
  656. data/src/include/errors.h +156 -39
  657. data/src/include/extract.h +11 -0
  658. data/src/include/herb.h +6 -0
  659. data/src/include/lexer/erb_openers.h +29 -0
  660. data/src/include/lexer/lexer_peek_helpers.h +8 -1
  661. data/src/include/lexer/lexer_struct.h +4 -0
  662. data/src/include/lexer/token.h +3 -0
  663. data/src/include/lexer/token_struct.h +8 -6
  664. data/src/include/lib/hb_allocator.h +2 -1
  665. data/src/include/lib/hb_array.h +2 -0
  666. data/src/include/lib/hb_foreach.h +1 -1
  667. data/src/include/lib/hb_string.h +7 -0
  668. data/src/include/parser/dot_notation.h +1 -1
  669. data/src/include/parser/foreign_content_elements.h +32 -0
  670. data/src/include/parser/parser.h +16 -30
  671. data/src/include/parser/parser_helpers.h +13 -11
  672. data/src/include/parser/parser_options.h +41 -0
  673. data/src/include/prism/ruby_parser.h +6 -0
  674. data/src/include/util/html_elements.h +85 -0
  675. data/src/include/util/html_util.h +1 -0
  676. data/src/include/version.h +1 -1
  677. data/src/lexer/lexer_peek_helpers.c +15 -1
  678. data/src/lexer/token.c +83 -1
  679. data/src/lexer.c +196 -12
  680. data/src/lib/hb_allocator.c +2 -0
  681. data/src/lib/hb_array.c +26 -0
  682. data/src/lib/hb_string.c +26 -0
  683. data/src/main.c +1 -1
  684. data/src/parser/dot_notation.c +4 -3
  685. data/src/parser/match_tags.c +66 -1
  686. data/src/parser/parser_helpers.c +69 -37
  687. data/src/parser.c +606 -383
  688. data/src/parser_options_defaults.c +30 -0
  689. data/src/prism/prism_helpers.c +3 -0
  690. data/src/prism/ruby_parser.c +49 -0
  691. data/src/util/html_util.c +11 -58
  692. data/src/visitor.c +77 -1
  693. data/templates/ext/herb/error_helpers.c.erb +1 -1
  694. data/templates/ext/herb/nodes.c.erb +18 -13
  695. data/templates/ext/herb/nodes.h.erb +2 -1
  696. data/templates/ext/herb/parser_options_helpers.c.erb +69 -0
  697. data/templates/ext/herb/parser_options_helpers.h.erb +11 -0
  698. data/templates/java/nodes.c.erb +20 -16
  699. data/templates/java/nodes.h.erb +3 -2
  700. data/templates/java/org/herb/ParserOptions.java.erb +24 -0
  701. data/templates/java/org/herb/ast/HelperRegistry.java.erb +67 -31
  702. data/templates/java/org/herb/ast/HelperType.java.erb +29 -0
  703. data/templates/java/org/herb/ast/Nodes.java.erb +11 -7
  704. data/templates/java/parser_options_helpers.c.erb +45 -0
  705. data/templates/java/parser_options_helpers.h.erb +10 -0
  706. data/templates/javascript/packages/client/src/state-kinds.ts.erb +49 -0
  707. data/templates/javascript/packages/client/src/state-operators.ts.erb +15 -0
  708. data/templates/javascript/packages/client/src/state-predicates.ts.erb +27 -0
  709. data/templates/javascript/packages/client/src/state-transforms.ts.erb +19 -0
  710. data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +47 -0
  711. data/templates/javascript/packages/core/src/built-in-components.ts.erb +67 -0
  712. data/templates/javascript/packages/core/src/errors.ts.erb +9 -3
  713. data/templates/javascript/packages/core/src/foreign-content-elements.ts.erb +18 -0
  714. data/templates/javascript/packages/core/src/html-constants.ts.erb +32 -0
  715. data/templates/javascript/packages/core/src/html-elements.ts.erb +41 -0
  716. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +11 -7
  717. data/templates/javascript/packages/core/src/nodes.ts.erb +82 -19
  718. data/templates/javascript/packages/core/src/parser-options.ts.erb +41 -0
  719. data/templates/javascript/packages/core/src/visitor.ts.erb +1 -1
  720. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +3 -3
  721. data/templates/javascript/packages/node/extension/nodes.cpp.erb +17 -12
  722. data/templates/javascript/packages/node/extension/nodes.h.erb +3 -3
  723. data/templates/javascript/packages/node/extension/parser_options_helpers.cpp.erb +41 -0
  724. data/templates/javascript/packages/node/extension/parser_options_helpers.h.erb +12 -0
  725. data/templates/lib/herb/action_view/helper_registry.rb.erb +77 -3
  726. data/templates/lib/herb/ast/nodes.rb.erb +16 -6
  727. data/templates/lib/herb/engine/slots/component_definitions.rb.erb +38 -0
  728. data/templates/lib/herb/engine/slots/state_kinds.rb.erb +37 -0
  729. data/templates/lib/herb/engine/slots/state_operators.rb.erb +26 -0
  730. data/templates/lib/herb/engine/slots/state_predicates.rb.erb +39 -0
  731. data/templates/lib/herb/engine/slots/state_transforms.rb.erb +27 -0
  732. data/templates/lib/herb/errors.rb.erb +34 -6
  733. data/templates/lib/herb/html/elements.rb.erb +12 -0
  734. data/templates/lib/herb/parser_options.rb.erb +32 -0
  735. data/templates/lib/herb/visitor.rb.erb +7 -1
  736. data/templates/rust/herb-printer/src/printer_visitor.rs.erb +14 -0
  737. data/templates/rust/src/action_view_helpers.rs.erb +38 -0
  738. data/templates/rust/src/ast/nodes.rs.erb +24 -31
  739. data/templates/rust/src/errors.rs.erb +1 -1
  740. data/templates/rust/src/nodes.rs.erb +58 -26
  741. data/templates/rust/src/parser_options.rs.erb +16 -0
  742. data/templates/rust/src/prism/generated_deserialize.rs.erb +109 -0
  743. data/templates/rust/src/prism/generated_prettyprint.rs.erb +153 -0
  744. data/templates/rust/src/union_types.rs.erb +7 -1
  745. data/templates/rust/src/visitor.rs.erb +1 -1
  746. data/templates/src/analyze/action_view/helper_registry.c.erb +22 -0
  747. data/templates/src/analyze/missing_end.c.erb +13 -5
  748. data/templates/src/ast/ast_nodes.c.erb +41 -2
  749. data/templates/src/diff/herb_diff_nodes.c.erb +44 -8
  750. data/templates/src/errors.c.erb +8 -3
  751. data/templates/src/include/analyze/action_view/helper_registry.h.erb +19 -0
  752. data/templates/src/include/analyze/herb_directive_kinds.h.erb +25 -0
  753. data/templates/src/include/errors.h.erb +3 -1
  754. data/templates/src/include/parser/foreign_content_elements.h.erb +23 -0
  755. data/templates/src/include/parser/parser_options.h.erb +21 -0
  756. data/templates/src/include/util/html_elements.h.erb +28 -0
  757. data/templates/src/parser_options_defaults.c.erb +9 -0
  758. data/templates/template.rb +564 -9
  759. data/templates/wasm/error_helpers.cpp.erb +4 -4
  760. data/templates/wasm/nodes.cpp.erb +12 -12
  761. data/templates/wasm/nodes.h.erb +3 -3
  762. data/templates/wasm/parser_options_helpers.cpp.erb +25 -0
  763. data/templates/wasm/parser_options_helpers.h.erb +12 -0
  764. metadata +306 -27
  765. data/config/action_view_helpers/actionview/form_helper/button.yml +0 -38
  766. data/config/action_view_helpers/actionview/form_helper/submit.yml +0 -35
  767. data/lib/herb/ast.rb +0 -7
  768. data/lib/herb/diff_operation.rb +0 -34
  769. data/lib/herb/diff_result.rb +0 -59
  770. data/lib/herb/engine/error_formatter.rb +0 -432
  771. data/lib/herb/engine/parser_error_overlay.rb +0 -773
  772. data/lib/herb/engine/validation_error_overlay.rb +0 -182
  773. data/lib/herb/engine/validation_errors.rb +0 -65
  774. data/lib/herb/engine/validator.rb +0 -80
  775. data/sig/herb/ast.rbs +0 -6
  776. data/sig/herb/diff_operation.rbs +0 -34
  777. data/sig/herb/diff_result.rbs +0 -34
  778. data/sig/herb/engine/debug.rbs +0 -38
  779. data/sig/herb/engine/error_formatter.rbs +0 -47
  780. data/sig/herb/engine/parser_error_overlay.rbs +0 -41
  781. data/sig/herb/engine/validation_error_overlay.rbs +0 -35
  782. data/sig/herb/engine/validation_errors.rbs +0 -45
  783. data/sig/herb/engine/validator.rbs +0 -41
  784. data/sig/herb_c_extension.rbs +0 -10
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, :file_timeout
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)
@@ -167,7 +173,7 @@ class Herb::CLI
167
173
  show_config
168
174
  exit(0)
169
175
  when "parse"
170
- 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)
171
177
  when "compile"
172
178
  compile_template
173
179
  when "render"
@@ -181,12 +187,7 @@ class Herb::CLI
181
187
  puts Herb.extract_html(file_content)
182
188
  exit(0)
183
189
  when "playground"
184
- require "bundler/inline"
185
-
186
- gemfile do
187
- source "https://rubygems.org"
188
- gem "lz_string"
189
- end
190
+ Herb.ensure_installed("lz_string")
190
191
 
191
192
  hash = LZString::UriSafe.compress(file_content)
192
193
  local_url = "http://localhost:5173"
@@ -322,18 +323,45 @@ class Herb::CLI
322
323
  self.track_whitespace = true
323
324
  end
324
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
+
325
334
  parser.on("--action-view-helpers", "Enable Action View helper detection (for parse command) (default: false)") do
326
335
  self.action_view_helpers = true
327
336
  end
328
337
 
329
- parser.on("--trim", "Enable trimming of leading/trailing whitespace (for compile/render commands)") do
330
- 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
331
340
  end
332
341
 
333
- 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
334
343
  self.optimize = true
335
344
  end
336
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
+
337
365
  parser.on("--tool TOOL", "Show config for specific tool: linter, formatter (for config command)") do |t|
338
366
  self.tool = t.to_sym
339
367
  end
@@ -393,7 +421,7 @@ class Herb::CLI
393
421
 
394
422
  case subcommand
395
423
  when "check"
396
- require_relative "action_view/render_analyzer"
424
+ require_relative "analysis/render_analyzer"
397
425
 
398
426
  path = File.expand_path(@file || ".")
399
427
 
@@ -402,12 +430,12 @@ class Herb::CLI
402
430
  exit(1)
403
431
  end
404
432
 
405
- analyzer = Herb::ActionView::RenderAnalyzer.new(path)
433
+ analyzer = Herb::Analysis::RenderAnalyzer.new(path)
406
434
  has_issues = analyzer.check!
407
435
 
408
436
  exit(has_issues ? 1 : 0)
409
437
  when "graph"
410
- require_relative "action_view/render_analyzer"
438
+ require_relative "analysis/render_analyzer"
411
439
 
412
440
  path = @file || "."
413
441
 
@@ -418,7 +446,7 @@ class Herb::CLI
418
446
 
419
447
  path = File.expand_path(path)
420
448
  project_root = File.directory?(path) ? path : config.project_root&.to_s || File.dirname(path)
421
- analyzer = Herb::ActionView::RenderAnalyzer.new(project_root)
449
+ analyzer = Herb::Analysis::RenderAnalyzer.new(project_root)
422
450
 
423
451
  if File.file?(path)
424
452
  analyzer.graph_file!(path)
@@ -426,6 +454,104 @@ class Herb::CLI
426
454
  analyzer.graph!
427
455
  end
428
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)
429
555
  exit(0)
430
556
  when "render"
431
557
  @file = @args[2]
@@ -438,14 +564,23 @@ class Herb::CLI
438
564
  bundle exec herb actionview [subcommand] [options]
439
565
 
440
566
  Subcommands:
441
- check [path] Check if render calls resolve to valid partial files
442
- graph [path] Show render dependency graph for a project or file
443
- 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
444
574
 
445
575
  Examples:
446
576
  bundle exec herb actionview check
447
577
  bundle exec herb actionview graph
448
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
449
584
  bundle exec herb actionview render app/views/posts/show.html.erb
450
585
 
451
586
  HELP
@@ -457,6 +592,325 @@ class Herb::CLI
457
592
  end
458
593
  end
459
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
+
460
914
  def actionview_render
461
915
  require "action_view"
462
916
 
@@ -671,25 +1125,59 @@ class Herb::CLI
671
1125
  exit(0)
672
1126
  end
673
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
+
674
1148
  def compile_template
675
1149
  require_relative "engine"
676
1150
 
677
1151
  begin
1152
+ source = file_content
678
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
+
679
1164
  options[:filename] = @file if @file
680
1165
  options[:escape] = no_escape ? false : true
681
1166
  options[:freeze] = true if freeze
682
- options[:strict] = strict.nil? || strict
1167
+ options[:parser_options] = (options[:parser_options] || {}).merge(strict: strict.nil? || strict)
683
1168
 
684
1169
  if debug
685
1170
  options[:debug] = true
686
1171
  options[:debug_filename] = @file if @file
687
1172
  end
688
1173
 
689
- options[:optimize] = true if optimize
690
- options[:trim] = true if trim
1174
+ options[:trim] = false if no_trim
691
1175
  options[:validate_ruby] = true
692
- 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
693
1181
 
694
1182
  if json
695
1183
  result = {
@@ -697,7 +1185,7 @@ class Herb::CLI
697
1185
  source: engine.src,
698
1186
  filename: engine.filename,
699
1187
  bufvar: engine.bufvar,
700
- strict: options[:strict],
1188
+ strict: options[:parser_options][:strict],
701
1189
  }
702
1190
 
703
1191
  puts result.to_json
@@ -722,7 +1210,7 @@ class Herb::CLI
722
1210
  else
723
1211
  puts e.compiled_source if e.compiled_source
724
1212
  puts
725
- puts e.message
1213
+ puts e.formatted_errors(highlight: Herb::Colors.enabled?) || e.message
726
1214
  end
727
1215
 
728
1216
  exit(1)
@@ -737,7 +1225,7 @@ class Herb::CLI
737
1225
  elsif silent
738
1226
  puts "Failed"
739
1227
  else
740
- puts e.message
1228
+ puts e.formatted_errors(highlight: Herb::Colors.enabled?) || e.message
741
1229
  end
742
1230
 
743
1231
  exit(1)
@@ -760,37 +1248,67 @@ class Herb::CLI
760
1248
  end
761
1249
  end
762
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
+
763
1266
  def render_template
764
1267
  require_relative "engine"
765
1268
 
766
1269
  begin
1270
+ source = file_content
767
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
+
768
1276
  options[:filename] = @file if @file
769
1277
  options[:escape] = no_escape ? false : true
770
1278
  options[:freeze] = true if freeze
771
- options[:strict] = strict.nil? || strict
1279
+ options[:parser_options] = (options[:parser_options] || {}).merge(strict: strict.nil? || strict)
772
1280
 
773
1281
  if debug
774
1282
  options[:debug] = true
775
1283
  options[:debug_filename] = @file if @file
776
1284
  end
777
1285
 
778
- options[:optimize] = true if optimize
779
- options[:trim] = true if trim
1286
+ options[:trim] = false if no_trim
780
1287
 
781
- 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)
782
1296
  compiled_code = engine.src
783
1297
 
784
1298
  rendered_output = eval(compiled_code)
785
1299
 
1300
+ print_warnings(slot_visitor&.warnings || []) unless json
1301
+
786
1302
  if json
787
1303
  result = {
788
1304
  success: true,
789
1305
  output: rendered_output,
790
1306
  filename: engine.filename,
791
- strict: options[:strict],
1307
+ strict: options[:parser_options][:strict],
792
1308
  }
793
1309
 
1310
+ result[:slots] = slot_visitor.schema if slot_visitor
1311
+
794
1312
  puts result.to_json
795
1313
  elsif silent
796
1314
  puts "Success"
@@ -810,7 +1328,7 @@ class Herb::CLI
810
1328
  elsif silent
811
1329
  puts "Failed"
812
1330
  else
813
- puts e.message
1331
+ puts e.formatted_errors(highlight: Herb::Colors.enabled?) || e.message
814
1332
  end
815
1333
 
816
1334
  exit(1)