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)