lib/css_parser/parser.rb in css_parser-1.17.1 vs lib/css_parser/parser.rb in css_parser-1.19.0
- old
+ new
@@ -1,7 +1,9 @@
# frozen_string_literal: true
+require 'strscan'
+
module CssParser
# Exception class used for any errors encountered while downloading remote files.
class RemoteFileError < IOError; end
# Exception class used if a request is made to load a CSS file more than once.
@@ -14,11 +16,12 @@
# When calling Parser#new there are some configuaration options:
# [<tt>absolute_paths</tt>] Convert relative paths to absolute paths (<tt>href</tt>, <tt>src</tt> and <tt>url('')</tt>. Boolean, default is <tt>false</tt>.
# [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
# [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
class Parser
- USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
+ USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)".freeze
+ RULESET_TOKENIZER_RX = /\s+|\\{2,}|\\?[{}\s"]|[()]|.[^\s"{}()\\]*/.freeze
STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
# Initial parsing
RE_AT_IMPORT_RULE = /@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s)]*)["']?\)?([\w\s,^\]()]*)\)?[;\n]?/.freeze
@@ -131,11 +134,11 @@
# Load @imported CSS
if @options[:import]
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
media_types = []
if (media_string = import_rule[-1])
- media_string.split(/,/).each do |t|
+ media_string.split(',').each do |t|
media_types << CssParser.sanitize_media_query(t) unless t.empty?
end
else
media_types = [:all]
end
@@ -162,28 +165,61 @@
block = ignore_pattern(block, RE_AT_IMPORT_RULE, options)
parse_block_into_rule_sets!(block, options)
end
- # Add a CSS rule by setting the +selectors+, +declarations+ and +media_types+.
+ # Add a CSS rule by setting the +selectors+, +declarations+
+ # and +media_types+. Optional pass +filename+ , +offset+ for source
+ # reference too.
#
- # +media_types+ can be a symbol or an array of symbols.
- def add_rule!(selectors, declarations, media_types = :all)
- rule_set = RuleSet.new(selectors, declarations)
- add_rule_set!(rule_set, media_types)
- rescue ArgumentError => e
- raise e if @options[:rule_set_exceptions]
+ # +media_types+ can be a symbol or an array of symbols. default to :all
+ # optional fields for source location for source location
+ # +filename+ can be a string or uri pointing to the file or url location.
+ # +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
+ def add_rule!(*args, selectors: nil, block: nil, filename: nil, offset: nil, media_types: :all) # rubocop:disable Metrics/ParameterLists
+ if args.any?
+ media_types = nil
+ if selectors || block || filename || offset || media_types
+ raise ArgumentError, "don't mix positional and keyword arguments arguments"
+ end
+
+ warn '[DEPRECATION] `add_rule!` with positional arguments is deprecated. ' \
+ 'Please use keyword arguments instead.', uplevel: 1
+
+ case args.length
+ when 2
+ selectors, block = args
+ when 3
+ selectors, block, media_types = args
+ else
+ raise ArgumentError
+ end
+ end
+
+ begin
+ rule_set = RuleSet.new(
+ selectors: selectors, block: block,
+ offset: offset, filename: filename
+ )
+
+ add_rule_set!(rule_set, media_types)
+ rescue ArgumentError => e
+ raise e if @options[:rule_set_exceptions]
+ end
end
# Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
#
# +filename+ can be a string or uri pointing to the file or url location.
# +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
# +media_types+ can be a symbol or an array of symbols.
def add_rule_with_offsets!(selectors, declarations, filename, offset, media_types = :all)
- rule_set = OffsetAwareRuleSet.new(filename, offset, selectors, declarations)
- add_rule_set!(rule_set, media_types)
+ warn '[DEPRECATION] `add_rule_with_offsets!` is deprecated. Please use `add_rule!` instead.', uplevel: 1
+ add_rule!(
+ selectors: selectors, block: declarations, media_types: media_types,
+ filename: filename, offset: offset
+ )
end
# Add a CssParser RuleSet object.
#
# +media_types+ can be a symbol or an array of symbols.
@@ -327,15 +363,19 @@
current_media_query = String.new
current_declarations = String.new
# once we are in a rule, we will use this to store where we started if we are capturing offsets
rule_start = nil
- offset = nil
+ start_offset = nil
+ end_offset = nil
- block.scan(/\s+|\\{2,}|\\?[{}\s"]|[()]|.[^\s"{}()\\]*/) do |token|
+ scanner = StringScanner.new(block)
+ until scanner.eos?
# save the regex offset so that we know where in the file we are
- offset = Regexp.last_match.offset(0) if options[:capture_offsets]
+ start_offset = scanner.pos
+ token = scanner.scan(RULESET_TOKENIZER_RX)
+ end_offset = scanner.pos
if token.start_with?('"') # found un-escaped double quote
in_string = !in_string
end
@@ -358,15 +398,18 @@
in_declarations -= 1
current_declarations.strip!
unless current_declarations.empty?
+ add_rule_options = {
+ selectors: current_selectors, block: current_declarations,
+ media_types: current_media_queries
+ }
if options[:capture_offsets]
- add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
- else
- add_rule!(current_selectors, current_declarations, current_media_queries)
+ add_rule_options.merge!(filename: options[:filename], offset: rule_start..end_offset)
end
+ add_rule!(**add_rule_options)
end
current_selectors = String.new
current_declarations = String.new
@@ -421,22 +464,25 @@
else
# if we are in a selector, add the token to the current selectors
current_selectors << token
# mark this as the beginning of the selector unless we have already marked it
- rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
+ rule_start = start_offset if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
end
end
# check for unclosed braces
return unless in_declarations > 0
- unless options[:capture_offsets]
- return add_rule!(current_selectors, current_declarations, current_media_queries)
+ add_rule_options = {
+ selectors: current_selectors, block: current_declarations,
+ media_types: current_media_queries
+ }
+ if options[:capture_offsets]
+ add_rule_options.merge!(filename: options[:filename], offset: rule_start..end_offset)
end
-
- add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
+ add_rule!(**add_rule_options)
end
# Load a remote CSS file.
#
# You can also pass in file://test.css
@@ -450,10 +496,12 @@
opts = {base_uri: nil, media_types: :all}
if options.is_a? Hash
opts.merge!(options)
else
+ warn '[DEPRECATION] `load_uri!` with positional arguments is deprecated. ' \
+ 'Please use keyword arguments instead.', uplevel: 1
opts[:base_uri] = options if options.is_a? String
opts[:media_types] = deprecated if deprecated
end
if uri.scheme == 'file' or uri.scheme.nil?
@@ -476,10 +524,12 @@
opts = {base_dir: nil, media_types: :all}
if options.is_a? Hash
opts.merge!(options)
else
+ warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
+ 'Please use keyword arguments instead.', uplevel: 1
opts[:base_dir] = options if options.is_a? String
opts[:media_types] = deprecated if deprecated
end
file_name = File.expand_path(file_name, opts[:base_dir])
@@ -499,9 +549,11 @@
opts = {base_dir: nil, media_types: :all}
if options.is_a? Hash
opts.merge!(options)
else
+ warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
+ 'Please use keyword arguments instead.', uplevel: 1
opts[:base_dir] = options if options.is_a? String
opts[:media_types] = deprecated if deprecated
end
add_block!(src, opts)