Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 css parser document #164

Open
wants to merge 12 commits into
base: v2
Choose a base branch
from
44 changes: 26 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Ruby CSS Parser [![Build Status](https://github.com/premailer/css_parser/workflows/Run%20css_parser%20CI/badge.svg)](https://github.com/ojab/css_parser/actions?query=workflow%3A%22Run+css_parser+CI%22) [![Gem Version](https://badge.fury.io/rb/css_parser.svg)](https://badge.fury.io/rb/css_parser)

Load, parse and cascade CSS rule sets in Ruby.
Load, parse and cascade CSS rule sets in Ruby.

If you are looking for a pure css stylesheet parser/tokenizer/lexer have a look at [crass](https://rubygems.org/gems/crass) or [syntax_tree-css](https://rubygems.org/gems/syntax_tree-css)

# Setup

Expand All @@ -10,35 +12,41 @@ gem install css_parser

# Usage

You initiate a document `CssParser::Document.new` and you can start to load it with css. Main methods to add css are: load_uri! (load url and follows @imports based on the full url), load_file! (loads file and follows @imports based on path from file imported) and load_string! (load a block of css). All of these apis tries to absolute all urls.

CssParser::Document -> Wrapper to holds all the rules on one block
CssParser::RuleSet -> Wrapper to hold each use like `.a, .b { color: hotpink; }`. notice this example has two selectors `.a` and `.b`


```Ruby
require 'css_parser'
include CssParser

parser = CssParser::Parser.new
parser.load_uri!('http://example.com/styles/style.css')
document = CssParser::Document.new
document.load_uri!('http://example.com/styles/style.css')

parser = CssParser::Parser.new
parser.load_uri!('file://home/user/styles/style.css')
document = CssParser::Document.new
document.load_uri!('file://home/user/styles/style.css')

# load a remote file, setting the base_uri and media_types
parser.load_uri!('../style.css', {base_uri: 'http://example.com/styles/inc/', media_types: [:screen, :handheld]})
document.load_uri!('../style.css', {base_uri: 'http://example.com/styles/inc/', media_types: [:screen, :handheld]})

# load a local file, setting the base_dir and media_types
parser.load_file!('print.css', '~/styles/', :print)
document.load_file!('print.css', '~/styles/', :print)

# load a string
parser = CssParser::Parser.new
parser.load_string! 'a { color: hotpink; }'
document = CssParser::Document.new
document.load_string! 'a { color: hotpink; }'

# lookup a rule by a selector
parser.find_by_selector('#content')
document.find_by_selector('#content')
#=> 'font-size: 13px; line-height: 1.2;'

# lookup a rule by a selector and media type
parser.find_by_selector('#content', [:screen, :handheld])
document.find_by_selector('#content', [:screen, :handheld])

# iterate through selectors by media type
parser.each_selector(:screen) do |selector, declarations, specificity|
document.each_selector(:screen) do |selector, declarations, specificity|
...
end

Expand All @@ -47,24 +55,24 @@ css = <<-EOT
body { margin: 0 1em; }
EOT

parser.add_block!(css)
document.add_block!(css)

# output all CSS rules in a single stylesheet
parser.to_s
document.to_s
=> #content { font-size: 13px; line-height: 1.2; }
body { margin: 0 1em; }

# capturing byte offsets within a file
parser.load_uri!('../style.css', {base_uri: 'http://example.com/styles/inc/', capture_offsets: true)
content_rule = parser.find_rule_sets(['#content']).first
document.load_uri!('../style.css', {base_uri: 'http://example.com/styles/inc/', capture_offsets: true)
content_rule = document.find_rule_sets(['#content']).first
content_rule.filename
#=> 'http://example.com/styles/styles.css'
content_rule.offset
#=> 10703..10752

# capturing byte offsets within a string
parser.load_string!('a { color: hotpink; }', {filename: 'index.html', capture_offsets: true)
content_rule = parser.find_rule_sets(['a']).first
document.load_string!('a { color: hotpink; }', {filename: 'index.html', capture_offsets: true)
content_rule = document.find_rule_sets(['a']).first
content_rule.filename
#=> 'index.html'
content_rule.offset
Expand Down
8 changes: 4 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ task :benchmark do
complex_css_path = fixtures_dir.join('complex.css').to_s.freeze

Benchmark.ips do |x|
x.report('import1.css loading') { CssParser::Parser.new.load_file!(import_css_path) }
x.report('complex.css loading') { CssParser::Parser.new.load_file!(complex_css_path) }
x.report('import1.css loading') { CssParser::Document.new.load_file!(import_css_path) }
x.report('complex.css loading') { CssParser::Document.new.load_file!(complex_css_path) }
end

puts

report = MemoryProfiler.report { CssParser::Parser.new.load_file!(import_css_path) }
report = MemoryProfiler.report { CssParser::Document.new.load_file!(import_css_path) }
puts "Loading `import1.css` allocated #{report.total_allocated} objects, #{report.total_allocated_memsize / 1024} KiB"

report = MemoryProfiler.report { CssParser::Parser.new.load_file!(complex_css_path) }
report = MemoryProfiler.report { CssParser::Document.new.load_file!(complex_css_path) }
puts "Loading `complex.css` allocated #{report.total_allocated} objects, #{report.total_allocated_memsize / 1024} KiB"
end
14 changes: 4 additions & 10 deletions lib/css_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
require 'crass'

require 'css_parser/version'
require 'css_parser/http_read_url'
require 'css_parser/file_resource'
require 'css_parser/rule_set'
require 'css_parser/rule_set/declarations'
require 'css_parser/regexps'
require 'css_parser/parser'
require 'css_parser/parser_fx'
require 'css_parser/document'

module CssParser
class Error < StandardError; end
Expand Down Expand Up @@ -58,8 +61,6 @@ class EmptyValueError < Error; end
# TODO: declaration_hashes should be able to contain a RuleSet
# this should be a Class method
def self.merge(*rule_sets)
@folded_declaration_cache = {}

# in case called like CssParser.merge([rule_set, rule_set])
rule_sets.flatten! if rule_sets[0].is_a?(Array)

Expand Down Expand Up @@ -154,11 +155,4 @@ def self.convert_uris(css, base_uri)
"url('#{uri}')"
end
end

def self.sanitize_media_query(raw)
mq = raw.to_s.gsub(/\s+/, ' ')
mq.strip!
mq = 'all' if mq.empty?
mq.to_sym
end
end
Loading