Commit 95afdfae authored by Luke Bennett's avatar Luke Bennett

Add HamlLint::Linter::NoPlainNodes linter

Add a simple haml_lint linter to report all plain nodes.
"Plain nodes" in HAML are scritpless plaintext leaf nodes.

Add haml-lint_todo to onboard new haml linters.
Excludes existing NoPlainNodes lints.
parent 712282cd
inherits_from:
- .haml-lint_todo.yml
# Whether to ignore frontmatter at the beginning of HAML documents for # Whether to ignore frontmatter at the beginning of HAML documents for
# frameworks such as Jekyll/Middleman # frameworks such as Jekyll/Middleman
skip_frontmatter: false skip_frontmatter: false
exclude: exclude:
- 'vendor/**/*' - 'vendor/**/*'
- 'spec/**/*' - 'spec/**/*'
require:
- './lib/haml_lint/linter/no_plain_nodes.rb'
linters: linters:
AltText: AltText:
......
This diff is collapsed.
# frozen_string_literal: true
require 'active_support/core_ext/array/grouping'
module HamlLint
class Linter
class NoPlainNodes < Linter
include LinterRegistry
def visit_tag(node)
if inline_plain_node?(node)
check_inline(node)
elsif !node.script.empty?
check_script(node)
else
check(node)
end
end
private
def check(node)
text_in_node(node).each { |string| record(node, string) }
end
def check_inline(node)
text = inline_text(node)
record(node, text) unless text.empty?
end
def check_script(node)
text = inline_text(node)
record(node, text) unless text.start_with?('=') || text.empty?
end
# Build an array of all strings in child text nodes.
# non text nodes are nil, where we'll split the sentences.
def text_in_node(node)
texts = node.children.map do |child|
child.text.strip if text_node?(child)
end
texts.split(nil).map { |sentence| sentence.join(' ') unless sentence.empty? }.compact
end
# Removes a node's attributes and tag from the source code,
# returning the inline text of a node.
def inline_text(node)
text = node.source_code.gsub("%#{node.tag_name}", '')
attributes = node.attributes_source.map(&:last)
attributes.each { |attribute| text = text.gsub(attribute, '') }
text.strip
end
def record(node, string)
record_lint(node, message(string))
end
def message(string)
"`#{string}` is a plain node. Please use an i18n method like `#{fixed(string)}`"
end
def fixed(string)
"= _('#{string}')"
end
def inline_plain_node?(node)
node.children.empty? && node.script.empty?
end
def plain_node?(node)
node.is_a?(::HamlLint::Tree::PlainNode)
end
def text_node?(node)
return false unless plain_node?(node)
!node.text.empty?
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require 'haml_lint'
require 'haml_lint/spec'
describe HamlLint::Linter::NoPlainNodes do
include_context 'linter'
context 'reports when a tag has an inline plain node' do
let(:haml) { '%tag Hello Tanuki' }
let(:message) { "`Hello Tanuki` is a plain node. Please use an i18n method like `= _('Hello Tanuki')`" }
it { is_expected.to report_lint message: message }
end
context 'reports when a tag has multiline plain nodes' do
let(:haml) { <<-HAML }
%tag
Hello
Tanuki
HAML
it { is_expected.to report_lint count: 1 }
end
context 'reports when a tag has an inline plain node with interpolation' do
let(:haml) { '%tag Hello #{"Tanuki"}!' } # rubocop:disable Lint/InterpolationCheck
it { is_expected.to report_lint }
end
context 'does not report when a tag has an inline script' do
let(:haml) { '%tag= "Hello Tanuki"' }
it { is_expected.not_to report_lint }
end
context 'does not report when a tag is empty' do
let(:haml) { '%tag' }
it { is_expected.not_to report_lint }
end
context 'reports multiple when a tag has multiline plain nodes split by non-text nodes' do
let(:haml) { <<-HAML }
%tag
Hello
.split-node There
Tanuki
HAML
it { is_expected.to report_lint count: 3 }
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment