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

Generate Sass Sourcemaps #79

Merged
merged 39 commits into from
Aug 4, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
bbe30b5
ignoring IDE specific files
Harald-LB Sep 12, 2018
d080f49
Temporary debug message to verify that sassC is used.
Harald-LB Sep 12, 2018
89943ef
introduced "fake-sass" for sass-load-path handling.
Harald-LB Sep 15, 2018
396cfc9
introduced "fake-sass" for sass-load-path handling.
Harald-LB Sep 15, 2018
d99d8d7
introduced "fake-sass" for sass-load-path handling.
Harald-LB Sep 17, 2018
b36e262
Clean diff
pathawks Sep 17, 2018
fdd353c
pre_render Hook added.
Harald-LB Sep 21, 2018
e92befc
source map now generated as default.
Harald-LB Sep 30, 2018
4099e37
Update fake-sass.rb
DirtyF Jan 23, 2019
ab9e96b
Update scss.rb
DirtyF Jan 23, 2019
d29e89e
Update source_map_page.rb
DirtyF Jan 23, 2019
f25767e
Remove trailing whitespace
ashmaroli Jan 24, 2019
61f447d
implemented ashmarolis suggestions.
Harald-LB Feb 11, 2019
db771f5
implemented ashmarolis suggestions.
ashmaroli Feb 11, 2019
d564a3f
appease Rubocop
DirtyF Feb 11, 2019
a28aece
editorial changes as suggested by ashmaroli.
Harald-LB Feb 11, 2019
7e1e046
remove extra whitespace
DirtyF Feb 11, 2019
75c5217
Merge branch 'sassc-src_map' of https://github.com/Harald-LB/jekyll-s…
Harald-LB Feb 11, 2019
fb6e8e1
Testing for at least "style" and "load_path" keys as suggested by ash…
Harald-LB Feb 11, 2019
0b83867
Comments now up to `100` chars, as suggested by ashmaroli.
Harald-LB Feb 11, 2019
4905eab
Merge branch 'master' into sassc-src_map
ashmaroli Feb 11, 2019
a208cf7
Call Jekyll.logger as used in Jekyll Core codebase
ashmaroli Feb 11, 2019
851d6b4
cleanup comments
ashmaroli Feb 11, 2019
713e08b
:pages type instances responds to :converters
ashmaroli Feb 20, 2019
bff5dca
Refer to the Scss converter class explicitly
ashmaroli Feb 20, 2019
4ef327c
Reduce log-level from :warn to :debug
ashmaroli Feb 20, 2019
dd62bf6
Designate helper methods as `private`
ashmaroli Feb 20, 2019
de48bbf
Update comments
ashmaroli Feb 20, 2019
2394463
source_map_page gets data hash from parent page
ashmaroli Feb 21, 2019
ec01aa9
fix generated inspect string
ashmaroli Feb 21, 2019
9512fb4
Add some documentation about the availability of sourcemaps and the a…
Harald-LB Feb 21, 2019
9a1b191
Clarified the use of sass config options. "source_map_contents" is al…
Harald-LB Feb 22, 2019
cb055f8
Typo corrected.
Harald-LB Feb 22, 2019
42c87d4
Clean up documentation for source maps
ashmaroli Feb 22, 2019
4077128
Added an option to disable source maps generation.
Harald-LB Feb 24, 2019
2b6ee96
cleanup docs
ashmaroli Feb 24, 2019
d580add
refactor #sourcemap_required?
ashmaroli Apr 4, 2019
06e805f
Merge branch 'master' into sassc-src_map
ashmaroli Aug 4, 2019
4950e7f
Expect compact CSS by default
ashmaroli Aug 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions lib/fake-sass.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

# This module helps to get rid of the now deprecated
# [ruby-sass](https://github.com/sass/ruby-sass).
#
# Some modules used in jekyll depend on the function `Sass.load_paths`
# from ruby-sass. (see for example `Jekyll::Theme.configure_sass`).
#
# This module provides a workaround when ruby-sass is not installed
# by faking this functionality...
#
# Please note that this module should never be installed __together__ with ruby-sass.
#
#
module Sass
# The global load paths for Sass files. This is meant for plugins and
# libraries to register the paths to their Sass stylesheets to that they may
# be `@imported`. This load path is used by every instance of {Sass::Engine}.
# They are lower-precedence than any load paths passed in via the
# {file:SASS_REFERENCE.md#load_paths-option `:load_paths` option}.
#
# If the `SASS_PATH` environment variable is set,
# the initial value of `load_paths` will be initialized based on that.
# The variable should be a colon-separated list of path names
# (semicolon-separated on Windows).
#
# Note that files on the global load path are never compiled to CSS
# themselves, even if they aren't partials. They exist only to be imported.
#
# @example
# Sass.load_paths << File.dirname(__FILE__ + '/sass')
# @return [Array<String, Pathname, Sass::Importers::Base>]
def self.load_paths
@load_paths ||= if ENV["SASS_PATH"]
ENV["SASS_PATH"].split(File::PATH_SEPARATOR)
else
[]
end
Harald-LB marked this conversation as resolved.
Show resolved Hide resolved
end
end
1 change: 1 addition & 0 deletions lib/jekyll-sass-converter.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require "fake-sass"
require "jekyll-sass-converter/version"
require "jekyll/converters/scss"
require "jekyll/converters/sass"
Expand Down
150 changes: 138 additions & 12 deletions lib/jekyll/converters/scss.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require "sassc"
require "jekyll/utils"
require "jekyll/source_map_page"

module Jekyll
module Converters
Expand All @@ -12,8 +13,86 @@ class Scss < Converter
safe true
priority :low

##
# This hook is triggered just before the method
# {#convert(content)} is executed, it associates
# the Scss (and Sass) converters with their
# respective sass_page objects.
Jekyll::Hooks.register :pages, :pre_render do |page|
if page.respond_to? :converters
page.converters.each do |converter|
converter.associate_page(page) if converter.is_a?(Scss)
end
end
end

##
# This hook is triggered just after the method
# {#convert(content)} is executed, it dissociates
# the Scss (and Sass) converters with their
# respective sass_page objects.
Jekyll::Hooks.register :pages, :post_render do |page|
if page.respond_to? :converters
page.converters.each do |converter|
converter.dissociate_page(page) if converter.is_a?(Scss)
end
end
end

ALLOWED_STYLES = %w(nested expanded compact compressed).freeze

##
# Associate this Converter with the "page" object that manages
# input and output files for this converter.
#
# Note: changing the associated sass_page-object during the live time of
# this Converter-object may result in inconsistent results.
#
# @param [Jekyll:Page] page the sass_page-object for which this object acts as converter.
def associate_page(page)
if @sass_page
Jekyll.logger.warn("Sass-converter",
"sass_page re-assigned: #{@sass_page.name} to #{page.name}")
dissociate_page(page)
return
end
@sass_page = page
end

##
# Dissociate this Converter with the "page" object.
#
# @param [Jekyll:Page] page the sass_page-object
# for which this object has acted as a converter.
def dissociate_page(page)
unless page.equal?(@sass_page)
Jekyll.logger.warn("Sass-converter",
"dissociating a page that was never associated #{page.name}")
end

@source_map_page = nil
@sass_page = nil
@site = nil
end

def associate_page_failed?
!sass_page
end

# @attr_reader [Jekyll:Page] sass_page the Page-object for which this
# object acts as a converter.
attr_reader :sass_page

def site
sass_page.site unless associate_page_failed?
end

def source_map_page
return if associate_page_failed?

@source_map_page ||= SourceMapPage.new(sass_page)
end

def matches(ext)
ext =~ %r!^\.scss$!i
end
Expand All @@ -38,12 +117,7 @@ def jekyll_sass_configuration

def sass_build_configuration_options(overrides)
if safe?
{
:load_paths => sass_load_paths,
:syntax => syntax,
:style => sass_style,
:cache => false,
}
overrides
else
Jekyll::Utils.symbolize_hash_keys(
Jekyll::Utils.deep_merge_hashes(
Expand Down Expand Up @@ -77,8 +151,10 @@ def sass_dir_relative_to_site_source
Jekyll.sanitized_path(site_source, sass_dir)
end

def sass_load_paths
paths = user_sass_load_paths + [sass_dir_relative_to_site_source]
def sass_load_paths # rubocop:disable Metrics/AbcSize
Harald-LB marked this conversation as resolved.
Show resolved Hide resolved
paths = user_sass_load_paths +
[sass_dir_relative_to_site_source] +
Array(::Sass.load_paths)
ashmaroli marked this conversation as resolved.
Show resolved Hide resolved

if safe?
# Sanitize paths to prevent any attack vectors (.e.g. `/**/*`)
Expand Down Expand Up @@ -110,23 +186,73 @@ def add_charset?
!!jekyll_sass_configuration["add_charset"]
end

# The name of the input scss (or sass) file.
# This information will be used for error reporting
# and will written into the source map file as main source.
# @return [String] the name of the input file or
# "stdin" if #associate_page failed
def filename
return "stdin" if associate_page_failed?

sass_page.name
end

# The name of the generated css file.
# This information will be written into the
# source map file as a backward reference to the input
# @return [String] the name of the css file or
# "stdin.css" if #associate_page failed
Harald-LB marked this conversation as resolved.
Show resolved Hide resolved
def output_path
return "stdin.css" if associate_page_failed?

sass_page.basename + ".css"
end

# The name of the generated source map file.
# This information will be written into the
# css file to reference to the source map.
# @return [String] the name of the css file or
# "" if #associate_page failed
def source_map_file
return "" if associate_page_failed?

sass_page.basename + ".css.map"
end

def sass_configs
sass_build_configuration_options(
"syntax" => syntax,
"cache" => allow_caching?,
"load_paths" => sass_load_paths
:style => sass_style,
:syntax => syntax,
:filename => filename,
:output_path => output_path,
:source_map_file => source_map_file,
:load_paths => sass_load_paths,
:source_map_contents => true
)
end

def convert(content)
output = ::SassC::Engine.new(content.dup, sass_configs).render
config = sass_configs
config[:omit_source_map_url] = true if associate_page_failed?
engine = ::SassC::Engine.new(content.dup, config)
output = engine.render
generate_source_map(engine)
replacement = add_charset? ? '@charset "UTF-8";' : ""
output.sub(BYTE_ORDER_MARK, replacement)
rescue ::SassC::SyntaxError => e
line = e.instance_variable_get(:@line)
raise SyntaxError, "#{e} on line #{line}"
end

def generate_source_map(engine)
return if associate_page_failed?

source_map_page.source_map(engine.source_map)
site.pages << source_map_page
rescue ::SassC::NotRenderedError => e
Jekyll.logger.warn("could not generate source map #{e.message} => #{e.cause}")
end

private

def site_source
Expand Down
38 changes: 38 additions & 0 deletions lib/jekyll/source_map_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module Jekyll
# A Jekyll::Page subclass to manage the source map file associated
# with a given scss-css @css_page.
#
class SourceMapPage < Page
# Initialize a new SourceMapPage.
#
# @param [Jekyll:Page] css_page the Page object that manages the css file.
def initialize(css_page)
@site = css_page.site
@dir = css_page.dir
@name = css_page.basename + ".css.map"

process(@name)
Jekyll::Hooks.trigger :pages, :post_init, self
end

def source_map(map)
self.output = map
end

def ext
".map"
end

def asset_file?
true
end

##
# @return[String] the object as a debug String.
def inspect
"#<Jekyll:SourceMapPage @name=#{name.inspect}>"
end
end
end
12 changes: 7 additions & 5 deletions spec/scss_converter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ def converter(overrides = {})
end

context "when building configurations" do
it "allow caching in unsafe mode" do
expect(converter.sass_configs[:cache]).to be_truthy
end
# Caching is no more a feature with sassC
# it "allow caching in unsafe mode" do
# expect(converter.sass_configs[:cache]).to be_truthy
# end

it "set the load paths to the _sass dir relative to site source" do
expect(converter.sass_configs[:load_paths]).to eql([source_dir("_sass")])
Expand Down Expand Up @@ -108,6 +109,7 @@ def converter(overrides = {})
end

it "only contains :syntax, :cache, :style, and :load_paths keys" do
skip("what is the reason to limit the possible options to those listed here?")
ashmaroli marked this conversation as resolved.
Show resolved Hide resolved
expect(verter.sass_configs.keys).to eql([:load_paths, :syntax, :style, :cache])
end
end
Expand Down Expand Up @@ -147,7 +149,7 @@ def converter(overrides = {})
end

it "imports SCSS partial" do
expect(File.read(test_css_file)).to eql(compressed(".half {\n width: 50%; }\n"))
expect(File.read(test_css_file)).to eql(".half{width:50%}\n\n/*# sourceMappingURL=main.css.map */")
end

it "uses a compressed style" do
Expand Down Expand Up @@ -188,7 +190,7 @@ def converter(overrides = {})

it "brings in the grid partial" do
site.process
expect(File.read(test_css_file)).to eql("a {\n color: #999999; }\n")
expect(File.read(test_css_file)).to eql("a { color: #999999; }\n\n/*# sourceMappingURL=main.css.map */")
end

context "with the sass_dir specified twice" do
Expand Down