better-html

repository·main·Indexed 19 days ago

https://github.com/shopify/better-html

A deprecated Ruby gem designed to introduce HTML-aware ERB parsing and runtime safety validations for Rails templates. It provides tools for validating HTML structure, preventing unsafe interpolation in tag or attribute names, and managing an Abstract Syntax Tree (AST) for templates. This library has been superseded by Herb Tools.

Tokens
2K
Snippets
9
Records
13
Agent score
66%

What's inside better-html

  1. Migrate from better_html to Herb Tools

    main

    The better_html gem is deprecated and no longer actively maintained. It has been superseded by the tools and guidance available at https://herb-tools.dev/.

    To migrate, you should:

    1. Review the modern concepts and guidance at https://herb-tools.dev/.
    2. Introduce the recommended modern tooling (linters and consolidated tools) alongside or in place of better_html.
    3. Plan a transition from better_html's runtime checks to the modern ecosystem's template safety and escaping practices.
  2. Configure BetterHtml settings

    main

    Global configuration is managed via BetterHtml.config. You can configure it using a block in an initializer or by loading a YAML file.

    Using a configuration block

    # config/initializers/better_html.rb
    BetterHtml.configure do |config|
      config.allow_single_quoted_attributes = false
    end

    Using a YAML file

    # config/initializers/better_html.rb
    BetterHtml.config = BetterHtml::Config.new(YAML.load_file(file_path, permitted_classes: [Regexp]))
    # config/initializers/better_html.rb
    BetterHtml.configure do |config|
      config.allow_single_quoted_attributes = false
    end
  3. Understand BetterHtml error hierarchy

    main

    The library uses a specific hierarchy of error classes to categorize different types of failures:

    • BetterHtml::HtmlError: The base error class for general HTML-related issues.
    • BetterHtml::InterpolatorError: A subclass of HtmlError used for issues occurring during the interpolation phase.
      • BetterHtml::DontInterpolateHere: Raised when interpolation is attempted in a location where it is explicitly disallowed.
      • BetterHtml::UnsafeHtmlError: Raised when unsafe HTML content is detected during interpolation.
    • BetterHtml::ParserError: A specialized error for syntax or structural issues during the parsing phase, containing positional metadata (line, column, position).
  4. Use the `html_attributes` helper

    main

    To use the html_attributes helper (which provides safer attribute insertion), include BetterHtml::Helpers in your ApplicationHelper:

    module ApplicationHelper
      include BetterHtml::Helpers
    
      # ...
    end
    module ApplicationHelper
      include BetterHtml::Helpers
    
      ...
    end
  5. Configure HTML validation rules

    main

    The behavior of the BetterHtml::BetterErb runtime checks is controlled via the BetterHtml.config object. You can customize validation using the following configuration keys:

    • disable_parser_validation: If set to true, the library will skip checking for general parser errors.
    • partial_tag_name_pattern: A regular expression that tag names must match to be considered valid.
    • partial_attribute_name_pattern: A regular expression that attribute names must match to be considered valid.
    • allow_single_quoted_attributes: A boolean that, if false, prevents the use of single quotes (') for attribute values.
    • allow_unquoted_attributes: A boolean that, if false, prevents the use of unquoted attribute values.
  6. Troubleshoot HTML validation errors in ERB templates

    main

    When using BetterHtml::BetterErb, the library performs runtime validation of your HTML to ensure it follows specific patterns and is well-formed. If validation fails, it raises one of two error types:

    1. BetterHtml::HtmlError: Raised when the HTML structure is invalid (e.g., unclosed tags, invalid tag names, or invalid attribute formats) or when the parser encounters syntax errors.
    2. BetterHtml::DontInterpolateHere: Raised when you attempt to use ERB interpolation (<%= ... %>) in a location where it is not safe (e.g., inside an HTML tag name or attribute name).

    Common Error Scenarios:

    • Open Tags: If a document ends while a tag is still open, a BetterHtml::HtmlError is raised with the message "Detected an open tag at the end of this document.".
    • Invalid Tag/Attribute Names: If a tag or attribute name does not match the patterns defined in your configuration, a BetterHtml::HtmlError is raised.
    • Quote/Unquoted Attributes: If your configuration disallows single-quoted or unquoted attributes and they are detected, a BetterHtml::HtmlError is raised.
    • Unsafe Interpolation: Attempting to interpolate Ruby code into sensitive HTML areas (like inside a tag name) triggers BetterHtml::DontInterpolateHere.
  7. Initialize the BetterHtml::Parser

    main

    To parse a template, instantiate BetterHtml::Parser with a Parser::Source::Buffer object. You must specify the template_language to determine how the parser handles interpolation.

    Supported template_language values:

    • :html (default): Uses Tokenizer::HtmlErb for ERB-style interpolation.
    • :javascript: Uses Tokenizer::JavascriptErb for JavaScript-style ERB interpolation.
    • :lodash: Uses Tokenizer::HtmlLodash for Lodash-style interpolation.

    If an unsupported language is provided, an ArgumentError is raised.

    parser = BetterHtml::Parser.new(buffer, template_language: :html)
  8. Access the BetterHtml configuration object

    main

    The BetterHtml.config method returns the current configuration instance. If no configuration has been initialized, it automatically creates a new instance of BetterHtml::Config. You can also manually set the configuration using BetterHtml.config = <config_object>.

    config = BetterHtml.config
    # or
    BetterHtml.configure { |c| ... }
  9. Configure BetterHtml using the configure block

    main

    You can configure the BetterHtml module by calling BetterHtml.configure and yielding a block. The block receives the BetterHtml.config object, which allows you to set up the library's settings. This is the standard way to initialize settings in a Rails initializer or at the start of your application.

    BetterHtml.configure do |config|
      # Set configuration options here
    end
  10. Access the Abstract Syntax Tree (AST) with BetterHtml::Parser

    main

    Once initialized, you can access the parsed structure of the template via the ast method. This returns a BetterHtml::AST::Node representing the document root.

    You can also use nodes_with_type(*type) to quickly retrieve all child nodes that match specific node types (e.g., :tag, :text, :erb).

    ast = parser.ast
    
    # Find all tag nodes
    tags = parser.nodes_with_type(:tag)
  11. Handle parsing errors in BetterHtml::Parser

    main

    If the underlying tokenizer encounters issues, you can retrieve them using the parser_errors method. This returns an array of BetterHtml::Parser::Error objects. Each error contains a descriptive message and a location object indicating where the error occurred in the source buffer.

    errors = parser.parser_errors
    errors.each do |error|
      puts "Error: #{error.message} at #{error.location}"
    end