mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
Merge branch 'master' into extend
Conflicts: lib/sass/engine.rb lib/sass/scss/parser.rb
This commit is contained in:
commit
298862b0f3
42 changed files with 866 additions and 156 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
3.0.0.beta.1
|
||||
3.0.0.beta.3
|
||||
|
|
|
@ -3,10 +3,20 @@
|
|||
* Table of contents
|
||||
{:toc}
|
||||
|
||||
## 3.0.0.beta.3
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.3).
|
||||
|
||||
There were no changes made to Haml between beta 2 and beta 3.
|
||||
|
||||
## 3.0.0.beta.2
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.2).
|
||||
|
||||
* Make {Haml::Helpers#capture_haml capture\_haml} faster when using `:ugly`.
|
||||
|
||||
* Add an `RDFa` doctype shortcut.
|
||||
|
||||
## 3.0.0.beta.1
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.1).
|
||||
|
@ -263,13 +273,17 @@ Thanks to [Josh Peek](http://joshpeek.com/).
|
|||
* The `puts` helper has been removed.
|
||||
Use {Haml::Helpers#haml\_concat} instead.
|
||||
|
||||
## 2.2.23 (Unreleased)
|
||||
## 2.2.23
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.23).
|
||||
|
||||
* Don't crash when `rake gems` is run in Rails with Haml installed.
|
||||
Thanks to [Florian Frank](http://github.com/flori).
|
||||
|
||||
* Don't remove `\n` in filters with interpolation.
|
||||
|
||||
* Silence those annoying `"regexp match /.../n against to UTF-8 string"` warnings.
|
||||
|
||||
## 2.2.22
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.22).
|
||||
|
|
|
@ -771,6 +771,10 @@ the following doctypes are supported:
|
|||
: XHTML Mobile 1.2<br/>
|
||||
`<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">`
|
||||
|
||||
`!!! RDFa`
|
||||
: XHTML+RDFa 1.0<br/>
|
||||
`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">`
|
||||
|
||||
When the [`:format`](#format) option is set to `:html4`,
|
||||
the following doctypes are supported:
|
||||
|
||||
|
|
|
@ -3,7 +3,75 @@
|
|||
* Table of contents
|
||||
{:toc}
|
||||
|
||||
## 3.0.0.beta.2 (Unreleased)
|
||||
## 3.0.0.beta.4
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Variables are now allowed as arguments to `url()`.
|
||||
|
||||
* `#{}` interpolation is now allowed within `url()`.
|
||||
|
||||
## 3.0.0.beta.3
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.3).
|
||||
|
||||
### `@import` in Sass
|
||||
|
||||
The Sass `@import` statement now allows non-CSS files to be specified with quotes,
|
||||
for similarity with the SCSS syntax. For example, `@import "foo.sass"`
|
||||
Will now import the `foo.sass` file, rather than compiling to `@import "foo.sass";`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Make sure nested comments use the proper prefix when `sass-convert` outputs Sass.
|
||||
|
||||
* Allow comments to begin with ` *` in all cases.
|
||||
|
||||
## 3.0.0.beta.2
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.2).
|
||||
|
||||
### Stylesheet Updating Speed
|
||||
|
||||
Several caching layers were added to Sass's stylesheet updater.
|
||||
This means that it should run significantly faster.
|
||||
This benefit will be seen by people using Sass in development mode
|
||||
with Rails, Rack, and Merb,
|
||||
as well as people using `sass --watch` from the command line,
|
||||
and to a lesser (but still significant) extent `sass --update`.
|
||||
Thanks to [thedarkone](http://github.com/thedarkone).
|
||||
|
||||
### New Sass Functions for Introspection
|
||||
|
||||
Several new functions were added to make it easier to have
|
||||
more flexible arguments to mixins and to enable deprecation
|
||||
of obsolete APIs.
|
||||
|
||||
* {Sass::Script::Functions#type_of `type-of`} -- Returns the type of a value.
|
||||
* {Sass::Script::Functions#unit `unit`} --
|
||||
Returns the units associated with a number.
|
||||
* {Sass::Script::Functions#unitless `unitless`} --
|
||||
Returns whether a number has units or not.
|
||||
* {Sass::Script::Functions#comparable `comparable`} --
|
||||
Returns whether two numbers can be added or compared.
|
||||
|
||||
### `@warn`
|
||||
|
||||
A new directive `@warn` has been added that allows Sass libraries to emit warnings.
|
||||
This can be used to issue deprecation warnings, discourage sloppy use of mixins, etc.
|
||||
`@warn` takes a single argument: a SassScript expression that will be
|
||||
displayed on the console along with a stylesheet trace for locating the warning.
|
||||
For example:
|
||||
|
||||
@mixin blue-text {
|
||||
@warn "The blue-text mixin is deprecated. Use new-blue-text instead.";
|
||||
color: #00f;
|
||||
}
|
||||
|
||||
Warnings may be silenced with the new `--quiet` command line option,
|
||||
or the corresponding {file:SASS_REFERENCE.md#quiet-option `:quiey` Sass option}.
|
||||
This option will also affect warnings printed by Sass itself.
|
||||
Warnings are off by default in the Rails, Rack, and Merb production environments.
|
||||
|
||||
### `sass-convert`
|
||||
|
||||
|
@ -26,6 +94,23 @@ Note that since underscores may still be used in place of hyphens
|
|||
when referring to mixins and variables,
|
||||
this won't cause any backwards-incompatibilities.
|
||||
|
||||
#### Sass Comment Format
|
||||
|
||||
When converting to the indented syntax,
|
||||
each line of comments past the first begins with either `//` (for Sass comments)
|
||||
or ` *` (for CSS comments).
|
||||
This prevents some whitespace errors.
|
||||
|
||||
### Minor Changes
|
||||
|
||||
Indented-syntax `/*` comments may now include `*` on lines beyond the first,
|
||||
which will get stripped out of the output.
|
||||
This means that the following will render nicely:
|
||||
|
||||
/* Foo
|
||||
* Bar
|
||||
* Baz
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* Include `;` when rendering nested properties.
|
||||
|
@ -53,11 +138,8 @@ this won't cause any backwards-incompatibilities.
|
|||
* Prevent multiline CSS selectors from getting cut off
|
||||
in certain rare cases.
|
||||
|
||||
### `@import` in Sass
|
||||
|
||||
The Sass `@import` statement now allows non-CSS files to be specified with quotes,
|
||||
for similarity with the SCSS syntax. For example, `@import "foo.sass"`
|
||||
Will now import the `foo.sass` file, rather than compiling to `@import "foo.sass";`.
|
||||
* Improve the parsing of mixin argument lists.
|
||||
This mostly means that error messages will be clearer.
|
||||
|
||||
### CSS Parsing
|
||||
|
||||
|
@ -567,7 +649,9 @@ and the hex representation (shortened to the three-letter version if possible).
|
|||
and `tealbang(12)` now renders as `tealbang(12)`
|
||||
rather than `teal bang(12)`.
|
||||
|
||||
## 2.2.23 (Unreleased)
|
||||
## 2.2.23
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.23).
|
||||
|
||||
* Don't crash when `rake gems` is run in Rails with Sass installed.
|
||||
Thanks to [Florian Frank](http://github.com/flori).
|
||||
|
@ -579,6 +663,9 @@ and the hex representation (shortened to the three-letter version if possible).
|
|||
{file:SASS_REFERENCE.md#full_exception `:full_exception option`} is enabled,
|
||||
print the full exception rather than raising it.
|
||||
|
||||
* Fix a bug with a weird interaction with Haml, DataMapper, and Rails 3
|
||||
that caused some tag helpers to go into infinite recursion.
|
||||
|
||||
## 2.2.22
|
||||
|
||||
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.22).
|
||||
|
|
|
@ -287,6 +287,9 @@ Available options are:
|
|||
Currently, this just means that strings in mixin arguments
|
||||
are treated as though they were in [an `=` context](#sass-script-strings).
|
||||
|
||||
{#quiet-option} `:quiet`
|
||||
: When set to true, causes warnings to be disabled.
|
||||
|
||||
## CSS Extensions
|
||||
|
||||
### Nested Rules
|
||||
|
@ -898,8 +901,7 @@ and `_colors.scss` would be imported.
|
|||
The `@debug` directive prints the value of a SassScript expression
|
||||
to the standard error output stream.
|
||||
It's useful for debugging Sass files
|
||||
that have complicated SassScript going on,
|
||||
or for printing warnings in libraries.
|
||||
that have complicated SassScript going on.
|
||||
For example:
|
||||
|
||||
@debug 10em + 12em;
|
||||
|
@ -908,6 +910,33 @@ outputs:
|
|||
|
||||
Line 1 DEBUG: 22em
|
||||
|
||||
### `@warn`
|
||||
|
||||
The `@warn` directive prints the value of a SassScript expression
|
||||
to the standard error output stream.
|
||||
It's useful for libraries that need to warn users of deprecations
|
||||
or recovering from minor mixin usage mistakes.
|
||||
There are two major distinctions between `@warn` and `@debug`:
|
||||
|
||||
1. You can turn warnings off with the `--quiet` command-line option
|
||||
or the `:quiet` Sass option.
|
||||
2. A stylesheet trace will be printed out along with the message
|
||||
so that the user being warned can see where their styles caused the warning.
|
||||
|
||||
Usage Example:
|
||||
|
||||
@mixin adjust-location($x, $y) {
|
||||
@if unitless($x) {
|
||||
@warn "Assuming #{$x} to be in pixels";
|
||||
$x: 1px * $x;
|
||||
}
|
||||
@if unitless($y) {
|
||||
@warn "Assuming #{$y} to be in pixels";
|
||||
$y: 1px * $y;
|
||||
}
|
||||
position: relative; left: $x; top: $y;
|
||||
}
|
||||
|
||||
## Control Directives
|
||||
|
||||
SassScript supports basic control directives
|
||||
|
|
|
@ -276,6 +276,9 @@ END
|
|||
'Output style. Can be nested (default), compact, compressed, or expanded.') do |name|
|
||||
@options[:for_engine][:style] = name.to_sym
|
||||
end
|
||||
opts.on('-q', '--quiet', 'Silence warnings during compilation.') do
|
||||
@options[:for_engine][:quiet] = true
|
||||
end
|
||||
opts.on('-g', '--debug-info',
|
||||
'Emit extra information in the generated CSS that can be used by the FireSass Firebug plugin.') do
|
||||
@options[:for_engine][:debug_info] = true
|
||||
|
@ -592,6 +595,9 @@ END
|
|||
'By default, this is inferred from the input filename.',
|
||||
'If there is none, defaults to css.') do |name|
|
||||
@options[:from] = name.downcase.to_sym
|
||||
unless [:css, :scss, :sass, :sass2].include?(@options[:from])
|
||||
raise "Unknown format for sass-convert --from: #{name}"
|
||||
end
|
||||
end
|
||||
|
||||
opts.on('-T', '--to FORMAT',
|
||||
|
@ -599,6 +605,9 @@ END
|
|||
'By default, this is inferred from the output filename.',
|
||||
'If there is none, defaults to sass.') do |name|
|
||||
@options[:to] = name.downcase.to_sym
|
||||
unless [:scss, :sass].include?(@options[:to])
|
||||
raise "Unknown format for sass-convert --to: #{name}"
|
||||
end
|
||||
end
|
||||
|
||||
opts.on('-R', '--recursive',
|
||||
|
|
|
@ -513,7 +513,7 @@ MESSAGE
|
|||
# @param text [String] The string to sanitize
|
||||
# @return [String] The sanitized string
|
||||
def html_escape(text)
|
||||
text.to_s.gsub(/[\"><&]/n) {|s| HTML_ESCAPE[s]}
|
||||
Haml::Util.silence_warnings {text.to_s.gsub(/[\"><&]/n) {|s| HTML_ESCAPE[s]}}
|
||||
end
|
||||
|
||||
# Escapes HTML entities in `text`, but without escaping an ampersand
|
||||
|
|
|
@ -125,9 +125,8 @@ module ActionView
|
|||
@template_object.send :is_haml?
|
||||
end
|
||||
|
||||
unless defined?(ActionView::Helpers) && defined?(ActionView::Helpers::ActiveRecordInstanceTag)
|
||||
alias_method :content_tag_without_haml, :content_tag
|
||||
alias_method :content_tag, :content_tag_with_haml
|
||||
def content_tag(*args)
|
||||
content_tag_with_haml(*args)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -905,6 +905,7 @@ END
|
|||
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
|
||||
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
|
||||
when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
|
||||
when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
|
||||
when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
|
||||
else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ module Sass
|
|||
extend Haml::Version
|
||||
|
||||
# A string representing the version of Sass.
|
||||
# A more fine-grained representation is available from {Sass.version}.
|
||||
# A more fine-grained representation is available from {Haml::Version#version Sass.version}.
|
||||
VERSION = version[:string] unless defined?(Sass::VERSION)
|
||||
end
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ require 'sass/tree/if_node'
|
|||
require 'sass/tree/while_node'
|
||||
require 'sass/tree/for_node'
|
||||
require 'sass/tree/debug_node'
|
||||
require 'sass/tree/warn_node'
|
||||
require 'sass/tree/import_node'
|
||||
require 'sass/selector'
|
||||
require 'sass/environment'
|
||||
|
@ -166,9 +167,9 @@ module Sass
|
|||
# @return [String] The CSS
|
||||
# @raise [Sass::SyntaxError] if there's an error in the document
|
||||
def render
|
||||
to_tree.render
|
||||
return _to_tree.render unless @options[:quiet]
|
||||
Haml::Util.silence_haml_warnings {_to_tree.render}
|
||||
end
|
||||
|
||||
alias_method :to_css, :render
|
||||
|
||||
# Parses the document into its parse tree.
|
||||
|
@ -176,6 +177,13 @@ module Sass
|
|||
# @return [Sass::Tree::Node] The root of the parse tree.
|
||||
# @raise [Sass::SyntaxError] if there's an error in the document
|
||||
def to_tree
|
||||
return _to_tree unless @options[:quiet]
|
||||
Haml::Util.silence_haml_warnings {_to_tree}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _to_tree
|
||||
@template = check_encoding(@template) {|msg, line| raise Sass::SyntaxError.new(msg, :line => line)}
|
||||
|
||||
if @options[:syntax] == :scss
|
||||
|
@ -193,8 +201,6 @@ module Sass
|
|||
raise e
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tabulate(string)
|
||||
tab_str = nil
|
||||
comment_tab_str = nil
|
||||
|
@ -230,7 +236,7 @@ module Sass
|
|||
end
|
||||
|
||||
comment_tab_str ||= line_tab_str
|
||||
if try_comment(line, lines.last, tab_str * (lines.last.tabs + 1), comment_tab_str, index)
|
||||
if try_comment(line, lines.last, tab_str * lines.last.tabs, comment_tab_str, index)
|
||||
next
|
||||
else
|
||||
comment_tab_str = nil
|
||||
|
@ -252,7 +258,9 @@ END
|
|||
|
||||
def try_comment(line, last, tab_str, comment_tab_str, index)
|
||||
return unless last && last.comment?
|
||||
return unless line =~ /^#{tab_str}/
|
||||
# Nested comment stuff must be at least one whitespace char deeper
|
||||
# than the normal indentation
|
||||
return unless line =~ /^#{tab_str}\s/
|
||||
unless line =~ /^(?:#{comment_tab_str})(.*)$/
|
||||
raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
|
||||
Inconsistent indentation:
|
||||
|
@ -463,9 +471,13 @@ WARNING
|
|||
|
||||
# If value begins with url( or ",
|
||||
# it's a CSS @import rule and we don't want to touch it.
|
||||
if directive == "import" && value !~ /^(url\(|["'])/
|
||||
if directive == "import"
|
||||
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
|
||||
:line => @line + 1) unless line.children.empty?
|
||||
if (match = value.match(Sass::SCSS::RX::STRING) || value.match(Sass::SCSS::RX::URI)) &&
|
||||
!match.post_match.strip.empty? && match.post_match.strip[0] != ?,
|
||||
return Tree::DirectiveNode.new("@import #{value}")
|
||||
end
|
||||
value.split(/,\s*/).map do |f|
|
||||
f = $1 || $2 || $3 if f =~ Sass::SCSS::RX::STRING || f =~ Sass::SCSS::RX::URI
|
||||
Tree::ImportNode.new(f)
|
||||
|
@ -496,6 +508,12 @@ WARNING
|
|||
:line => @line + 1) unless line.children.empty?
|
||||
offset = line.offset + line.text.index(value).to_i
|
||||
Tree::ExtendNode.new(parse_interp(value, offset))
|
||||
elsif directive == "warn"
|
||||
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
|
||||
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
|
||||
:line => @line + 1) unless line.children.empty?
|
||||
offset = line.offset + line.text.index(value).to_i
|
||||
Tree::WarnNode.new(parse_script(value, :offset => offset))
|
||||
else
|
||||
Tree::DirectiveNode.new(line.text)
|
||||
end
|
||||
|
@ -584,6 +602,7 @@ WARNING
|
|||
end
|
||||
|
||||
return silent ? "//" : "/* */" if content.empty?
|
||||
content.each {|l| l.gsub!(/^\* /, '')}
|
||||
content.map! {|l| (l.empty? ? "" : " ") + l}
|
||||
content.first.gsub!(/^ /, '') unless removed_first
|
||||
content.last.gsub!(%r{ ?\*/ *$}, '')
|
||||
|
|
|
@ -23,7 +23,7 @@ module Sass
|
|||
@vars = {}
|
||||
@mixins = {}
|
||||
@parent = parent
|
||||
|
||||
@stack = [] unless parent
|
||||
set_var("important", Script::String.new("!important")) unless @parent
|
||||
end
|
||||
|
||||
|
@ -35,6 +35,52 @@ module Sass
|
|||
@options || (parent && parent.options) || {}
|
||||
end
|
||||
|
||||
# Push a new stack frame onto the mixin/include stack.
|
||||
#
|
||||
# @param frame_info [{Symbol => Object}]
|
||||
# Frame information has the following keys:
|
||||
#
|
||||
# `:filename`
|
||||
# : The name of the file in which the lexical scope changed.
|
||||
#
|
||||
# `:mixin`
|
||||
# : The name of the mixin in which the lexical scope changed,
|
||||
# or `nil` if it wasn't within in a mixin.
|
||||
#
|
||||
# `:line`
|
||||
# : The line of the file on which the lexical scope changed. Never nil.
|
||||
def push_frame(frame_info)
|
||||
if stack.last && stack.last[:prepared]
|
||||
stack.last.delete(:prepared)
|
||||
stack.last.merge!(frame_info)
|
||||
else
|
||||
stack.push(frame_info)
|
||||
end
|
||||
end
|
||||
|
||||
# Like \{#push\_frame}, but next time a stack frame is pushed,
|
||||
# it will be merged with this frame.
|
||||
#
|
||||
# @param frame_info [{Symbol => Object}] Same as for \{#push\_frame}.
|
||||
def prepare_frame(frame_info)
|
||||
push_frame(frame_info.merge(:prepared => true))
|
||||
end
|
||||
|
||||
# Pop a stack frame from the mixin/include stack.
|
||||
def pop_frame
|
||||
stack.pop if stack.last[:prepared]
|
||||
stack.pop
|
||||
end
|
||||
|
||||
# A list of stack frames in the mixin/include stack.
|
||||
# The last element in the list is the most deeply-nested frame.
|
||||
#
|
||||
# @return [Array<{Symbol => Object}>] The stack frames,
|
||||
# of the form passed to \{#push\_frame}.
|
||||
def stack
|
||||
@stack ||= @parent.stack
|
||||
end
|
||||
|
||||
class << self
|
||||
private
|
||||
|
||||
|
|
|
@ -76,8 +76,11 @@ module Sass
|
|||
return filename
|
||||
end
|
||||
|
||||
new_filename = find_full_path("#{filename}.sass", load_paths) unless was_scss
|
||||
new_filename ||= find_full_path("#{filename}.scss", load_paths) unless was_sass
|
||||
new_filename = nil
|
||||
load_paths.each do |load_path|
|
||||
new_filename ||= find_full_path("#{filename}.sass", load_path) unless was_scss
|
||||
new_filename ||= find_full_path("#{filename}.scss", load_path) unless was_sass
|
||||
end
|
||||
|
||||
return new_filename if new_filename
|
||||
unless was_sass || was_scss
|
||||
|
@ -134,7 +137,7 @@ END
|
|||
end
|
||||
end
|
||||
|
||||
def find_full_path(filename, load_paths)
|
||||
def find_full_path(filename, load_path)
|
||||
partial_name = File.join(File.dirname(filename), "_#{File.basename(filename)}")
|
||||
|
||||
if Pathname.new(filename).absolute?
|
||||
|
@ -144,13 +147,9 @@ END
|
|||
return nil
|
||||
end
|
||||
|
||||
load_paths.each do |path|
|
||||
[partial_name, filename].each do |name|
|
||||
full_path = File.join(path, name)
|
||||
if File.readable?(full_path)
|
||||
return full_path
|
||||
end
|
||||
end
|
||||
[partial_name, filename].each do |name|
|
||||
full_path = File.join(load_path, name)
|
||||
return full_path if File.readable?(full_path)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'rbconfig'
|
|||
|
||||
require 'sass'
|
||||
require 'sass/callbacks'
|
||||
require 'sass/plugin/staleness_checker'
|
||||
|
||||
module Sass
|
||||
# This module handles the compilation of Sass/SCSS files.
|
||||
|
@ -211,6 +212,8 @@ module Sass
|
|||
individual_files.each {|t, c| update_stylesheet(t, c)}
|
||||
|
||||
@checked_for_updates = true
|
||||
staleness_checker = StalenessChecker.new
|
||||
|
||||
template_locations.zip(css_locations).each do |template_location, css_location|
|
||||
|
||||
Dir.glob(File.join(template_location, "**", "*.s[ca]ss")).each do |file|
|
||||
|
@ -219,7 +222,7 @@ module Sass
|
|||
css = css_filename(name, css_location)
|
||||
|
||||
next if forbid_update?(name)
|
||||
if options[:always_update] || stylesheet_needs_update?(css, file)
|
||||
if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
|
||||
update_stylesheet file, css
|
||||
else
|
||||
run_not_updating_stylesheet file, css
|
||||
|
@ -374,33 +377,9 @@ module Sass
|
|||
name.sub(/^.*\//, '')[0] == ?_
|
||||
end
|
||||
|
||||
# Compass expects this to exist
|
||||
def stylesheet_needs_update?(css_file, template_file)
|
||||
return true unless File.exists?(css_file) && File.exists?(template_file)
|
||||
|
||||
css_mtime = File.mtime(css_file)
|
||||
File.mtime(template_file) > css_mtime ||
|
||||
dependencies(template_file).any?(&dependency_updated?(css_mtime))
|
||||
end
|
||||
|
||||
def dependency_updated?(css_mtime)
|
||||
lambda do |dep|
|
||||
begin
|
||||
File.mtime(dep) > css_mtime ||
|
||||
dependencies(dep).any?(&dependency_updated?(css_mtime))
|
||||
rescue Sass::SyntaxError
|
||||
# If there's an error finding depenencies, default to recompiling.
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def dependencies(filename)
|
||||
Files.tree_for(filename, engine_options).select {|n| n.is_a?(Tree::ImportNode)}.map do |n|
|
||||
next if n.full_filename =~ /\.css$/
|
||||
n.full_filename
|
||||
end.compact
|
||||
rescue Sass::SyntaxError => e
|
||||
[] # If the file has an error, we assume it has no dependencies
|
||||
StalenessChecker.stylesheet_needs_update?(css_file, template_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ unless defined?(Sass::MERB_LOADED)
|
|||
:css_location => root + '/public/stylesheets',
|
||||
:cache_location => root + '/tmp/sass-cache',
|
||||
:always_check => env != "production",
|
||||
:quiet => env != "production",
|
||||
:full_exception => env != "production")
|
||||
config = Merb::Plugins.config[:sass] || Merb::Plugins.config["sass"] || {}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ unless defined?(Sass::RAILS_LOADED)
|
|||
:css_location => Haml::Util.rails_root + '/public/stylesheets',
|
||||
:cache_location => Haml::Util.rails_root + '/tmp/sass-cache',
|
||||
:always_check => Haml::Util.rails_env != "production",
|
||||
:quiet => Haml::Util.rails_env != "production",
|
||||
:full_exception => Haml::Util.rails_env != "production")
|
||||
|
||||
if defined?(Rails.configuration) && defined?(Rails.configuration.middleware)
|
||||
|
|
123
lib/sass/plugin/staleness_checker.rb
Normal file
123
lib/sass/plugin/staleness_checker.rb
Normal file
|
@ -0,0 +1,123 @@
|
|||
module Sass
|
||||
module Plugin
|
||||
# The class handles `.s[ca]ss` file staleness checks via their mtime timestamps.
|
||||
#
|
||||
# To speed things up two level of caches are employed:
|
||||
#
|
||||
# * A class-level dependency cache which stores @import paths for each file.
|
||||
# This is a long-lived cache that is reused by every StalenessChecker instance.
|
||||
# * Two short-lived instance-level caches, one for file mtimes
|
||||
# and one for whether a file is stale during this particular run.
|
||||
# These are only used by a single StalenessChecker instance.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# * For a one-off staleness check of a single `.s[ca]ss` file,
|
||||
# the class-level {stylesheet_needs_update?} method
|
||||
# should be used.
|
||||
# * For a series of staleness checks (e.g. checking all files for staleness)
|
||||
# a StalenessChecker instance should be created,
|
||||
# and the instance-level \{#stylesheet\_needs\_update?} method should be used.
|
||||
# the caches should make the whole process significantly faster.
|
||||
# *WARNING*: It is important not to retain the instance for too long,
|
||||
# as its instance-level caches are never explicitly expired.
|
||||
class StalenessChecker
|
||||
@dependencies_cache = {}
|
||||
|
||||
class << self
|
||||
# @private
|
||||
attr_accessor :dependencies_cache
|
||||
end
|
||||
|
||||
# Creates a new StalenessChecker
|
||||
# for checking the staleness of several stylesheets at once.
|
||||
def initialize
|
||||
@dependencies = self.class.dependencies_cache
|
||||
|
||||
# Entries in the following instance-level caches are never explicitly expired.
|
||||
# Instead they are supposed to automaticaly go out of scope when a series of staleness checks
|
||||
# (this instance of StalenessChecker was created for) is finished.
|
||||
@mtimes, @dependencies_stale = {}, {}
|
||||
end
|
||||
|
||||
# Returns whether or not a given CSS file is out of date
|
||||
# and needs to be regenerated.
|
||||
#
|
||||
# @param css_file [String] The location of the CSS file to check.
|
||||
# @param template_file [String] The location of the Sass or SCSS template
|
||||
# that is compiled to `css_file`.
|
||||
def stylesheet_needs_update?(css_file, template_file)
|
||||
template_file = File.expand_path(template_file)
|
||||
|
||||
unless File.exists?(css_file) && File.exists?(template_file)
|
||||
@dependencies.delete(template_file)
|
||||
true
|
||||
else
|
||||
css_mtime = mtime(css_file)
|
||||
mtime(template_file) > css_mtime || dependencies_stale?(template_file, css_mtime)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns whether or not a given CSS file is out of date
|
||||
# and needs to be regenerated.
|
||||
#
|
||||
# The distinction between this method and the instance-level \{#stylesheet\_needs\_update?}
|
||||
# is that the instance method preserves mtime and stale-dependency caches,
|
||||
# so it's better to use when checking multiple stylesheets at once.
|
||||
#
|
||||
# @param css_file [String] The location of the CSS file to check.
|
||||
# @param template_file [String] The location of the Sass or SCSS template
|
||||
# that is compiled to `css_file`.
|
||||
def self.stylesheet_needs_update?(css_file, template_file)
|
||||
new.stylesheet_needs_update?(css_file, template_file)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dependencies_stale?(template_file, css_mtime)
|
||||
timestamps = @dependencies_stale[template_file] ||= {}
|
||||
timestamps.each_pair do |checked_css_mtime, is_stale|
|
||||
if checked_css_mtime <= css_mtime && !is_stale
|
||||
return false
|
||||
elsif checked_css_mtime > css_mtime && is_stale
|
||||
return true
|
||||
end
|
||||
end
|
||||
timestamps[css_mtime] = dependencies(template_file).any?(&dependency_updated?(css_mtime))
|
||||
end
|
||||
|
||||
def mtime(filename)
|
||||
@mtimes[filename] ||= File.mtime(filename)
|
||||
end
|
||||
|
||||
def dependencies(filename)
|
||||
stored_mtime, dependencies = @dependencies[filename]
|
||||
|
||||
if !stored_mtime || stored_mtime < mtime(filename)
|
||||
@dependencies[filename] = [mtime(filename), dependencies = compute_dependencies(filename)]
|
||||
end
|
||||
|
||||
dependencies
|
||||
end
|
||||
|
||||
def dependency_updated?(css_mtime)
|
||||
lambda do |dep|
|
||||
begin
|
||||
mtime(dep) > css_mtime || dependencies_stale?(dep, css_mtime)
|
||||
rescue Sass::SyntaxError
|
||||
# If there's an error finding depenencies, default to recompiling.
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def compute_dependencies(filename)
|
||||
Files.tree_for(filename, Plugin.engine_options).grep(Tree::ImportNode) do |n|
|
||||
File.expand_path(n.full_filename) unless n.full_filename =~ /\.css$/
|
||||
end.compact
|
||||
rescue Sass::SyntaxError => e
|
||||
[] # If the file has an error, we assume it has no dependencies
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,7 +5,12 @@ module Sass
|
|||
important || super
|
||||
end
|
||||
|
||||
def string(*args)
|
||||
def string(re, *args)
|
||||
if re == :uri
|
||||
return unless uri = scan(URI)
|
||||
return [:string, Script::String.new(uri)]
|
||||
end
|
||||
|
||||
return unless scan(STRING)
|
||||
[:string, Script::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
|
||||
end
|
||||
|
|
|
@ -105,6 +105,20 @@ module Sass::Script
|
|||
# \{#abs}
|
||||
# : Returns the absolute value of a number.
|
||||
#
|
||||
# ## Introspection Functions
|
||||
#
|
||||
# \{#type_of}
|
||||
# : Returns the type of a value.
|
||||
#
|
||||
# \{#unit}
|
||||
# : Returns the units associated with a number.
|
||||
#
|
||||
# \{#unitless}
|
||||
# : Returns whether a number has units or not.
|
||||
#
|
||||
# \{#comparable}
|
||||
# : Returns whether two numbers can be added or compared.
|
||||
#
|
||||
# These functions are described in more detail below.
|
||||
#
|
||||
# ## Adding Custom Functions
|
||||
|
@ -669,6 +683,71 @@ module Sass::Script
|
|||
Sass::Script::String.new(str.value, :string)
|
||||
end
|
||||
|
||||
# Inspects the type of the argument, returning it as an unquoted string.
|
||||
# For example:
|
||||
#
|
||||
# type-of(100px) => number
|
||||
# type-of(asdf) => string
|
||||
# type-of("asdf") => string
|
||||
# type-of(true) => bool
|
||||
# type-of(#fff) => color
|
||||
# type-of(blue) => color
|
||||
#
|
||||
# @param obj [Literal] The object to inspect
|
||||
# @return [String] The unquoted string name of the literal's type
|
||||
def type_of(obj)
|
||||
Sass::Script::String.new(obj.class.name.gsub(/Sass::Script::/,'').downcase)
|
||||
end
|
||||
|
||||
# Inspects the unit of the number, returning it as a quoted string.
|
||||
# Complex units are sorted in alphabetical order by numerator and denominator.
|
||||
# For example:
|
||||
#
|
||||
# unit(100) => ""
|
||||
# unit(100px) => "px"
|
||||
# unit(3em) => "em"
|
||||
# unit(10px * 5em) => "em*px"
|
||||
# unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
|
||||
#
|
||||
# @param number [Literal] The number to inspect
|
||||
# @return [String] The unit(s) of the number
|
||||
# @raise [ArgumentError] if `number` isn't a number
|
||||
def unit(number)
|
||||
assert_type number, :Number
|
||||
Sass::Script::String.new(number.unit_str, :string)
|
||||
end
|
||||
|
||||
# Inspects the unit of the number, returning a boolean indicating if it is unitless.
|
||||
# For example:
|
||||
#
|
||||
# unitless(100) => true
|
||||
# unitless(100px) => false
|
||||
#
|
||||
# @param number [Literal] The number to inspect
|
||||
# @return [Bool] Whether or not the number is unitless
|
||||
# @raise [ArgumentError] if `number` isn't a number
|
||||
def unitless(number)
|
||||
assert_type number, :Number
|
||||
Sass::Script::Bool.new(number.unitless?)
|
||||
end
|
||||
|
||||
# Returns true if two numbers are similar enough to be added, subtracted, or compared.
|
||||
# For example:
|
||||
#
|
||||
# comparable(2px, 1px) => true
|
||||
# comparable(100px, 3em) => false
|
||||
# comparable(10cm, 3mm) => true
|
||||
#
|
||||
# @param number1 [Number]
|
||||
# @param number2 [Number]
|
||||
# @return [Bool] indicating if the numbers can be compared.
|
||||
# @raise [ArgumentError] if `number1` or `number2` aren't numbers
|
||||
def comparable(number1, number2)
|
||||
assert_type number1, :Number
|
||||
assert_type number2, :Number
|
||||
Sass::Script::Bool.new(number1.comparable_to?(number2))
|
||||
end
|
||||
|
||||
# Converts a decimal number to a percentage.
|
||||
# For example:
|
||||
#
|
||||
|
|
|
@ -111,7 +111,7 @@ module Sass
|
|||
|
||||
# A hash of regular expressions that are used for tokenizing strings.
|
||||
#
|
||||
# The key is a [Symbol, Boolean] pair.
|
||||
# The key is a `[Symbol, Boolean]` pair.
|
||||
# The symbol represents which style of quotation to use,
|
||||
# while the boolean represents whether or not the string
|
||||
# is following an interpolated segment.
|
||||
|
@ -120,6 +120,8 @@ module Sass
|
|||
[:single, false] => string_re("'", "'"),
|
||||
[:double, true] => string_re('', '"'),
|
||||
[:single, true] => string_re('', "'"),
|
||||
[:uri, false] => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|(?=#\{))/,
|
||||
[:uri, true] => /(#{URLCHAR}*?)(#{W}\)|(?=#\{))/,
|
||||
}
|
||||
|
||||
# @param str [String, StringScanner] The source text to lex
|
||||
|
@ -216,8 +218,8 @@ module Sass
|
|||
end
|
||||
|
||||
variable || string(:double, false) || string(:single, false) || number ||
|
||||
color || bool || raw(URI) || raw(UNICODERANGE) || special_fun ||
|
||||
ident_op || ident || op
|
||||
color || bool || string(:uri, false) || raw(UNICODERANGE) ||
|
||||
special_fun || ident_op || ident || op
|
||||
end
|
||||
|
||||
def variable
|
||||
|
@ -243,7 +245,13 @@ module Sass
|
|||
def string(re, open)
|
||||
return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
|
||||
@interpolation_stack << re if @scanner[2].empty? # Started an interpolated section
|
||||
[:string, Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)]
|
||||
str =
|
||||
if re == :uri
|
||||
Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2].empty?}")
|
||||
else
|
||||
Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
|
||||
end
|
||||
[:string, str]
|
||||
end
|
||||
|
||||
def number
|
||||
|
|
|
@ -299,6 +299,30 @@ module Sass::Script
|
|||
end, num_units, den_units)
|
||||
end
|
||||
|
||||
# @param other [Number] A number to decide if it can be compared with this number.
|
||||
# @return [Boolean] Whether or not this number can be compared with the other.
|
||||
def comparable_to?(other)
|
||||
begin
|
||||
self.operate(other, :+)
|
||||
true
|
||||
rescue Sass::UnitConversionError
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a human readable representation of the units in this number.
|
||||
# For complex units this takes the form of:
|
||||
# numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
|
||||
# @return [String] a string that represents the units in this number
|
||||
def unit_str
|
||||
rv = numerator_units.sort.join("*")
|
||||
if denominator_units.any?
|
||||
rv << "/"
|
||||
rv << denominator_units.sort.join("*")
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def operate(other, operation)
|
||||
|
@ -343,15 +367,6 @@ module Sass::Script
|
|||
end
|
||||
end
|
||||
|
||||
def unit_str
|
||||
rv = numerator_units.join("*")
|
||||
if denominator_units.any?
|
||||
rv << "/"
|
||||
rv << denominator_units.join("*")
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
def normalize!
|
||||
return if unitless?
|
||||
@numerator_units, @denominator_units = sans_common_units(numerator_units, denominator_units)
|
||||
|
|
|
@ -97,12 +97,7 @@ module Sass
|
|||
# @return [Array<Script::Node>] The root nodes of the arguments.
|
||||
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
|
||||
def parse_mixin_definition_arglist
|
||||
args = []
|
||||
|
||||
if try_tok(:lparen)
|
||||
args = defn_arglist(false) || args
|
||||
assert_tok(:rparen)
|
||||
end
|
||||
args = defn_arglist!(false)
|
||||
assert_done
|
||||
|
||||
args.each do |k, v|
|
||||
|
@ -239,26 +234,33 @@ RUBY
|
|||
end
|
||||
end
|
||||
|
||||
def defn_arglist(must_have_default)
|
||||
line = @lexer.line
|
||||
offset = @lexer.offset + 1
|
||||
return unless c = try_tok(:const)
|
||||
var = Script::Variable.new(c.value)
|
||||
if tok = (try_tok(:colon) || try_tok(:single_eq))
|
||||
val = assert_expr(:concat)
|
||||
def defn_arglist!(must_have_default)
|
||||
return [] unless try_tok(:lparen)
|
||||
return [] if try_tok(:rparen)
|
||||
res = []
|
||||
loop do
|
||||
line = @lexer.line
|
||||
offset = @lexer.offset + 1
|
||||
c = assert_tok(:const)
|
||||
var = Script::Variable.new(c.value)
|
||||
if tok = (try_tok(:colon) || try_tok(:single_eq))
|
||||
val = assert_expr(:concat)
|
||||
|
||||
if tok.type == :single_eq
|
||||
val.context = :equals
|
||||
val.options = @options
|
||||
Script.equals_warning("mixin argument defaults", "$#{c.value}",
|
||||
val.to_sass, false, line, offset, @options[:filename])
|
||||
if tok.type == :single_eq
|
||||
val.context = :equals
|
||||
val.options = @options
|
||||
Script.equals_warning("mixin argument defaults", "$#{c.value}",
|
||||
val.to_sass, false, line, offset, @options[:filename])
|
||||
end
|
||||
must_have_default = true
|
||||
elsif must_have_default
|
||||
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
|
||||
end
|
||||
elsif must_have_default
|
||||
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
|
||||
res << [var, val]
|
||||
break unless try_tok(:comma)
|
||||
end
|
||||
|
||||
return [[var, val]] unless try_tok(:comma)
|
||||
[[var, val], *defn_arglist(val)]
|
||||
assert_tok(:rparen)
|
||||
res
|
||||
end
|
||||
|
||||
def fn_arglist
|
||||
|
|
|
@ -81,7 +81,7 @@ module Sass
|
|||
node << comment
|
||||
end
|
||||
|
||||
DIRECTIVES = Set[:mixin, :include, :debug, :for, :while, :if, :extend, :import, :media]
|
||||
DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :extend, :import, :media]
|
||||
|
||||
def directive
|
||||
return unless tok(/@/)
|
||||
|
@ -131,6 +131,10 @@ module Sass
|
|||
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
|
||||
end
|
||||
|
||||
def warn
|
||||
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
|
||||
end
|
||||
|
||||
def for
|
||||
tok!(/\$/)
|
||||
var = tok! IDENT
|
||||
|
|
|
@ -65,7 +65,8 @@ module Sass
|
|||
NAME = /#{NMCHAR}+/
|
||||
NUM = /[0-9]+|[0-9]*.[0-9]+/
|
||||
STRING = /#{STRING1}|#{STRING2}/
|
||||
URL = /((?:[!#%$&*-~]|#{NONASCII}|#{ESCAPE})*)/
|
||||
URLCHAR = /[!#%&*-~]|#{NONASCII}|#{ESCAPE}/
|
||||
URL = /(#{URLCHAR}*)/
|
||||
W = /[ \t\r\n\f]*/
|
||||
|
||||
# This is more liberal than the spec's definition,
|
||||
|
|
|
@ -59,15 +59,16 @@ module Sass::Tree
|
|||
else
|
||||
content.gsub!(/\n( \*|\/\/)/, "\n ")
|
||||
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
|
||||
sep = silent ? "\n//" : "\n *"
|
||||
if spaces >= 2
|
||||
content
|
||||
content.gsub(/\n /, sep)
|
||||
else
|
||||
content.gsub(/\n#{' ' * spaces}/, "\n ")
|
||||
content.gsub(/\n#{' ' * spaces}/, sep)
|
||||
end
|
||||
end
|
||||
|
||||
content.gsub!(/^/, ' ' * tabs)
|
||||
content.gsub!(/\A\/\*/, '//') if silent
|
||||
content.gsub!(/^/, ' ' * tabs)
|
||||
content.rstrip + "\n"
|
||||
end
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ module Sass
|
|||
protected
|
||||
|
||||
def to_src(tabs, opts, fmt)
|
||||
"#{' ' * tabs}@debug #{@expr.to_sass}#{semi fmt}\n"
|
||||
"#{' ' * tabs}@debug #{@expr.to_sass(opts)}#{semi fmt}\n"
|
||||
end
|
||||
|
||||
# Prints the expression to STDERR.
|
||||
|
|
|
@ -62,6 +62,7 @@ module Sass
|
|||
# @param environment [Sass::Environment] The lexical environment containing
|
||||
# variable and mixin values
|
||||
def perform!(environment)
|
||||
environment.push_frame(:filename => @filename, :line => @line)
|
||||
root = Sass::Files.tree_for(full_filename, @options)
|
||||
@template = root.template
|
||||
self.children = root.children
|
||||
|
@ -70,6 +71,8 @@ module Sass
|
|||
e.modify_backtrace(:filename => full_filename)
|
||||
e.add_backtrace(:filename => @filename, :line => @line)
|
||||
raise e
|
||||
ensure
|
||||
environment.pop_frame
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -65,13 +65,15 @@ module Sass::Tree
|
|||
# @raise [Sass::SyntaxError] if an incorrect number of arguments was passed
|
||||
# @see Sass::Tree
|
||||
def perform!(environment)
|
||||
original_env = environment
|
||||
original_env.push_frame(:filename => filename, :line => line)
|
||||
original_env.prepare_frame(:mixin => @name)
|
||||
raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.") unless mixin = environment.mixin(@name)
|
||||
|
||||
raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < @args.size
|
||||
Mixin #{@name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
|
||||
but #{@args.size} #{@args.size == 1 ? 'was' : 'were'} passed.
|
||||
END
|
||||
|
||||
environment = mixin.args.zip(@args).
|
||||
inject(Sass::Environment.new(mixin.environment)) do |env, ((var, default), value)|
|
||||
env.set_local_var(var.name,
|
||||
|
@ -93,6 +95,8 @@ END
|
|||
e.modify_backtrace(:mixin => @name, :line => @line)
|
||||
e.add_backtrace(:line => @line)
|
||||
raise e
|
||||
ensure
|
||||
original_env.pop_frame
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
41
lib/sass/tree/warn_node.rb
Normal file
41
lib/sass/tree/warn_node.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
module Sass
|
||||
module Tree
|
||||
# A dynamic node representing a Sass `@warn` statement.
|
||||
#
|
||||
# @see Sass::Tree
|
||||
class WarnNode < Node
|
||||
# @param expr [Script::Node] The expression to print
|
||||
def initialize(expr)
|
||||
@expr = expr
|
||||
super()
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def to_src(tabs, opts, fmt)
|
||||
"#{' ' * tabs}@warn #{@expr.to_sass(opts)}#{semi fmt}\n"
|
||||
end
|
||||
|
||||
# Prints the expression to STDERR with a stylesheet trace.
|
||||
#
|
||||
# @param environment [Sass::Environment] The lexical environment containing
|
||||
# variable and mixin values
|
||||
def _perform(environment)
|
||||
environment.push_frame(:filename => filename, :line => line)
|
||||
res = @expr.perform(environment)
|
||||
res = res.value if res.is_a?(Sass::Script::String)
|
||||
msg = "WARNING: #{res}\n"
|
||||
environment.stack.reverse.each_with_index do |entry, i|
|
||||
msg << " #{i == 0 ? "on" : "from"} line #{entry[:line]}" <<
|
||||
" of #{entry[:filename] || "an unknown file"}"
|
||||
msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
|
||||
msg << "\n"
|
||||
end
|
||||
Haml::Util.haml_warn msg
|
||||
[]
|
||||
ensure
|
||||
environment.pop_frame
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -251,9 +251,9 @@ SCSS
|
|||
|
||||
assert_renders <<SASS, <<SCSS
|
||||
// foo
|
||||
bar
|
||||
baz
|
||||
bang
|
||||
// bar
|
||||
// baz
|
||||
// bang
|
||||
|
||||
foo bar
|
||||
a: b
|
||||
|
@ -286,6 +286,50 @@ foo bar
|
|||
SASS
|
||||
end
|
||||
|
||||
def test_nested_silent_comments
|
||||
assert_renders <<SASS, <<SCSS
|
||||
foo
|
||||
bar: baz
|
||||
// bip bop
|
||||
// beep boop
|
||||
bang: bizz
|
||||
// bubble bubble
|
||||
// toil trouble
|
||||
SASS
|
||||
foo {
|
||||
bar: baz;
|
||||
// bip bop
|
||||
// beep boop
|
||||
bang: bizz;
|
||||
// bubble bubble
|
||||
// toil trouble
|
||||
}
|
||||
SCSS
|
||||
|
||||
assert_sass_to_scss <<SCSS, <<SASS
|
||||
foo {
|
||||
bar: baz;
|
||||
// bip bop
|
||||
// beep boop
|
||||
// bap blimp
|
||||
bang: bizz;
|
||||
// bubble bubble
|
||||
// toil trouble
|
||||
// gorp
|
||||
}
|
||||
SCSS
|
||||
foo
|
||||
bar: baz
|
||||
// bip bop
|
||||
beep boop
|
||||
bap blimp
|
||||
bang: bizz
|
||||
// bubble bubble
|
||||
toil trouble
|
||||
gorp
|
||||
SASS
|
||||
end
|
||||
|
||||
def test_loud_comments
|
||||
assert_renders <<SASS, <<SCSS
|
||||
/* foo
|
||||
|
@ -309,9 +353,9 @@ SCSS
|
|||
|
||||
assert_scss_to_sass <<SASS, <<SCSS
|
||||
/* foo
|
||||
bar
|
||||
baz
|
||||
bang
|
||||
* bar
|
||||
* baz
|
||||
* bang
|
||||
|
||||
foo bar
|
||||
a: b
|
||||
|
@ -337,9 +381,9 @@ SCSS
|
|||
|
||||
assert_renders <<SASS, <<SCSS
|
||||
/* foo
|
||||
bar
|
||||
baz
|
||||
bang
|
||||
* bar
|
||||
* baz
|
||||
* bang
|
||||
|
||||
foo bar
|
||||
a: b
|
||||
|
@ -354,12 +398,54 @@ foo bar {
|
|||
SCSS
|
||||
end
|
||||
|
||||
def test_nested_loud_comments
|
||||
assert_renders <<SASS, <<SCSS
|
||||
foo
|
||||
bar: baz
|
||||
/* bip bop
|
||||
* beep boop
|
||||
bang: bizz
|
||||
/* bubble bubble
|
||||
* toil trouble
|
||||
SASS
|
||||
foo {
|
||||
bar: baz;
|
||||
/* bip bop
|
||||
* beep boop */
|
||||
bang: bizz;
|
||||
/* bubble bubble
|
||||
* toil trouble */ }
|
||||
SCSS
|
||||
|
||||
assert_sass_to_scss <<SCSS, <<SASS
|
||||
foo {
|
||||
bar: baz;
|
||||
/* bip bop
|
||||
* beep boop
|
||||
* bap blimp */
|
||||
bang: bizz;
|
||||
/* bubble bubble
|
||||
* toil trouble
|
||||
* gorp */ }
|
||||
SCSS
|
||||
foo
|
||||
bar: baz
|
||||
/* bip bop
|
||||
beep boop
|
||||
bap blimp
|
||||
bang: bizz
|
||||
/* bubble bubble
|
||||
toil trouble
|
||||
gorp
|
||||
SASS
|
||||
end
|
||||
|
||||
def test_loud_comments_with_weird_indentation
|
||||
assert_scss_to_sass <<SASS, <<SCSS
|
||||
foo
|
||||
/* foo
|
||||
bar
|
||||
baz
|
||||
* bar
|
||||
* baz
|
||||
a: b
|
||||
SASS
|
||||
foo {
|
||||
|
@ -387,8 +473,8 @@ SASS
|
|||
def test_immediately_preceding_comments
|
||||
assert_renders <<SASS, <<SCSS
|
||||
/* Foo
|
||||
Bar
|
||||
Baz
|
||||
* Bar
|
||||
* Baz
|
||||
.foo#bar
|
||||
a: b
|
||||
SASS
|
||||
|
@ -401,8 +487,8 @@ SCSS
|
|||
|
||||
assert_renders <<SASS, <<SCSS
|
||||
// Foo
|
||||
Bar
|
||||
Baz
|
||||
// Bar
|
||||
// Baz
|
||||
=foo
|
||||
a: b
|
||||
SASS
|
||||
|
@ -595,8 +681,8 @@ SCSS
|
|||
end
|
||||
|
||||
def test_import_as_directive_in_sass
|
||||
assert_sass_to_sass '@import "foo.css"'
|
||||
assert_sass_to_sass '@import url(foo.css)'
|
||||
assert_equal "@import foo.css\n", to_sass('@import "foo.css"')
|
||||
assert_equal "@import foo.css\n", to_sass('@import url(foo.css)')
|
||||
end
|
||||
|
||||
def test_import_as_directive_in_scss
|
||||
|
|
|
@ -105,20 +105,20 @@ elephant.rawr
|
|||
rampages: excessively
|
||||
|
||||
/* actual multiline
|
||||
comment
|
||||
*comment
|
||||
|
||||
span.turkey
|
||||
isdinner: true
|
||||
|
||||
.turducken
|
||||
/* Sounds funny
|
||||
doesn't it
|
||||
* doesn't it
|
||||
chimera: not_really
|
||||
|
||||
#overhere
|
||||
bored: sorta
|
||||
/* it's for a good
|
||||
cause
|
||||
* cause
|
||||
better_than: thread_pools
|
||||
|
||||
#one_more
|
||||
|
|
|
@ -81,10 +81,10 @@ MSG
|
|||
"a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
|
||||
"a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
|
||||
"a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
|
||||
"=a(" => 'Invalid CSS after "(": expected ")", was ""',
|
||||
"=a(b)" => 'Invalid CSS after "(": expected ")", was "b)"',
|
||||
"=a(,)" => 'Invalid CSS after "(": expected ")", was ",)"',
|
||||
"=a(!)" => 'Invalid CSS after "(": expected ")", was "!)"',
|
||||
"=a(" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ""',
|
||||
"=a(b)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "b)"',
|
||||
"=a(,)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ",)"',
|
||||
"=a($)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "$)"',
|
||||
"=a($foo bar)" => 'Invalid CSS after "($foo ": expected ")", was "bar)"',
|
||||
"=foo\n bar: baz\n+foo" => ["Properties aren't allowed at the root of a document.", 2],
|
||||
"a-\#{$b\n c: d" => ['Invalid CSS after "a-#{$b": expected "}", was ""', 1],
|
||||
|
@ -107,6 +107,9 @@ MSG
|
|||
'@if' => "Invalid if directive '@if': expected expression.",
|
||||
'@while' => "Invalid while directive '@while': expected expression.",
|
||||
'@debug' => "Invalid debug directive '@debug': expected expression.",
|
||||
%Q{@debug "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath debug directives.",
|
||||
'@warn' => "Invalid warn directive '@warn': expected expression.",
|
||||
%Q{@warn "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath warn directives.",
|
||||
"/* foo\n bar\n baz" => "Inconsistent indentation: previous line was indented by 4 spaces, but this line was indented by 2 spaces.",
|
||||
|
||||
# Regression tests
|
||||
|
@ -937,7 +940,7 @@ SASS
|
|||
def test_equals_warning_for_mixin_args
|
||||
assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
|
||||
DEPRECATION WARNING:
|
||||
On line 1, character 6 of 'test_equals_warning_for_mixin_args_inline.sass'
|
||||
On line 1, character 10 of 'test_equals_warning_for_mixin_args_inline.sass'
|
||||
Setting mixin argument defaults with = has been deprecated and will be removed in version 3.2.
|
||||
Use "$arg: 1px" instead.
|
||||
|
||||
|
@ -1301,6 +1304,18 @@ foo {
|
|||
CSS
|
||||
end
|
||||
|
||||
def test_loud_comments_with_starred_lines
|
||||
assert_equal(<<CSS, render(<<SASS))
|
||||
/* This is a comment that
|
||||
* continues to the second line.
|
||||
* And even to the third! */
|
||||
CSS
|
||||
/* This is a comment that
|
||||
* continues to the second line.
|
||||
* And even to the third!
|
||||
SASS
|
||||
end
|
||||
|
||||
def test_comment_indentation_at_beginning_of_doc
|
||||
assert_equal <<CSS, render(<<SASS)
|
||||
/* foo
|
||||
|
@ -1709,6 +1724,58 @@ SASS
|
|||
end
|
||||
end
|
||||
|
||||
def test_warn_directive
|
||||
expected_warning = <<EXPECTATION
|
||||
WARNING: this is a warning
|
||||
on line 4 of test_warn_directive_inline.sass
|
||||
|
||||
WARNING: this is a mixin warning
|
||||
on line 2 of test_warn_directive_inline.sass, in `foo'
|
||||
from line 7 of test_warn_directive_inline.sass
|
||||
EXPECTATION
|
||||
assert_warning expected_warning do
|
||||
assert_equal <<CSS, render(<<SASS)
|
||||
bar {
|
||||
c: d; }
|
||||
CSS
|
||||
=foo
|
||||
@warn "this is a mixin warning"
|
||||
|
||||
@warn "this is a warning"
|
||||
bar
|
||||
c: d
|
||||
+foo
|
||||
SASS
|
||||
end
|
||||
end
|
||||
|
||||
def test_warn_directive_when_quiet
|
||||
assert_warning "" do
|
||||
assert_equal <<CSS, render(<<SASS, :quiet => true)
|
||||
CSS
|
||||
@warn "this is a warning"
|
||||
SASS
|
||||
end
|
||||
end
|
||||
|
||||
def test_warn_with_imports
|
||||
expected_warning = <<WARN
|
||||
WARNING: In the main file
|
||||
on line 1 of #{File.dirname(__FILE__)}/templates/warn.sass
|
||||
|
||||
WARNING: Imported
|
||||
on line 1 of #{File.dirname(__FILE__)}/templates/warn_imported.sass
|
||||
from line 2 of #{File.dirname(__FILE__)}/templates/warn.sass
|
||||
|
||||
WARNING: In an imported mixin
|
||||
on line 4 of #{File.dirname(__FILE__)}/templates/warn_imported.sass, in `emits-a-warning'
|
||||
from line 3 of #{File.dirname(__FILE__)}/templates/warn.sass
|
||||
WARN
|
||||
assert_warning expected_warning do
|
||||
renders_correctly "warn", :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"]
|
||||
end
|
||||
end
|
||||
|
||||
# Regression tests
|
||||
|
||||
def test_parens_in_mixins
|
||||
|
@ -1834,6 +1901,15 @@ a
|
|||
SASS
|
||||
end
|
||||
|
||||
def test_mixin_no_arg_error
|
||||
assert_raise(Sass::SyntaxError, 'Invalid CSS after "($bar,": expected variable name, was ")"') do
|
||||
render(<<SASS)
|
||||
=foo($bar,)
|
||||
bip: bap
|
||||
SASS
|
||||
end
|
||||
end
|
||||
|
||||
# Encodings
|
||||
|
||||
unless Haml::Util.ruby1_8?
|
||||
|
|
|
@ -505,6 +505,37 @@ The #options attribute is not set on this Sass::Script::String.
|
|||
MSG
|
||||
end
|
||||
|
||||
def test_type_of
|
||||
assert_equal("string", evaluate("type-of(\"asdf\")"))
|
||||
assert_equal("string", evaluate("type-of(asdf)"))
|
||||
assert_equal("number", evaluate("type-of(1px)"))
|
||||
assert_equal("bool", evaluate("type-of(true)"))
|
||||
assert_equal("color", evaluate("type-of(#fff)"))
|
||||
end
|
||||
|
||||
def test_unit
|
||||
assert_equal(%Q{""}, evaluate("unit(100)"))
|
||||
assert_equal(%Q{"px"}, evaluate("unit(100px)"))
|
||||
assert_equal(%Q{"em*px"}, evaluate("unit(10px * 5em)"))
|
||||
assert_equal(%Q{"em*px"}, evaluate("unit(5em * 10px)"))
|
||||
assert_equal(%Q{"em*px/cm*rem"}, evaluate("unit(10px * 5em / 30cm / 1rem)"))
|
||||
assert_error_message("#ff0000 is not a number for `unit'", "unit(#f00)")
|
||||
end
|
||||
|
||||
def test_unitless
|
||||
assert_equal(%Q{true}, evaluate("unitless(100)"))
|
||||
assert_equal(%Q{false}, evaluate("unitless(100px)"))
|
||||
assert_error_message("#ff0000 is not a number for `unitless'", "unitless(#f00)")
|
||||
end
|
||||
|
||||
def test_comparable
|
||||
assert_equal(%Q{true}, evaluate("comparable(2px, 1px)"))
|
||||
assert_equal(%Q{true}, evaluate("comparable(10cm, 3mm)"))
|
||||
assert_equal(%Q{false}, evaluate("comparable(100px, 3em)"))
|
||||
assert_error_message("#ff0000 is not a number for `comparable'", "comparable(#f00, 1px)")
|
||||
assert_error_message("#ff0000 is not a number for `comparable'", "comparable(1px, #f00)")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def evaluate(value)
|
||||
|
|
|
@ -14,7 +14,7 @@ class SassPluginTest < Test::Unit::TestCase
|
|||
FileUtils.mkdir tempfile_loc
|
||||
FileUtils.mkdir tempfile_loc(nil,"more_")
|
||||
set_plugin_opts
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
reset_mtimes
|
||||
end
|
||||
|
||||
|
@ -34,21 +34,21 @@ class SassPluginTest < Test::Unit::TestCase
|
|||
def test_no_update
|
||||
File.delete(tempfile_loc('basic'))
|
||||
assert_needs_update 'basic'
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
assert_stylesheet_updated 'basic'
|
||||
end
|
||||
|
||||
def test_update_needed_when_modified
|
||||
touch 'basic'
|
||||
assert_needs_update 'basic'
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
assert_stylesheet_updated 'basic'
|
||||
end
|
||||
|
||||
def test_update_needed_when_dependency_modified
|
||||
touch 'basic'
|
||||
assert_needs_update 'import'
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
assert_stylesheet_updated 'basic'
|
||||
assert_stylesheet_updated 'import'
|
||||
end
|
||||
|
@ -56,7 +56,7 @@ class SassPluginTest < Test::Unit::TestCase
|
|||
def test_update_needed_when_scss_dependency_modified
|
||||
touch 'scss_importee'
|
||||
assert_needs_update 'import'
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
assert_stylesheet_updated 'scss_importee'
|
||||
assert_stylesheet_updated 'import'
|
||||
end
|
||||
|
@ -64,14 +64,14 @@ class SassPluginTest < Test::Unit::TestCase
|
|||
def test_scss_update_needed_when_dependency_modified
|
||||
touch 'basic'
|
||||
assert_needs_update 'scss_import'
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
assert_stylesheet_updated 'basic'
|
||||
assert_stylesheet_updated 'scss_import'
|
||||
end
|
||||
|
||||
def test_full_exception_handling
|
||||
File.delete(tempfile_loc('bork1'))
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
File.open(tempfile_loc('bork1')) do |file|
|
||||
assert_equal(<<CSS.strip, file.read.split("\n")[0...6].join("\n"))
|
||||
/*
|
||||
|
@ -90,7 +90,7 @@ CSS
|
|||
Sass::Plugin.options[:full_exception] = false
|
||||
|
||||
File.delete(tempfile_loc('bork1'))
|
||||
assert_raise(Sass::SyntaxError) {Sass::Plugin.update_stylesheets}
|
||||
assert_raise(Sass::SyntaxError) {update_all_stylesheets!}
|
||||
ensure
|
||||
Sass::Plugin.options[:full_exception] = old_full_exception
|
||||
end
|
||||
|
@ -100,7 +100,7 @@ CSS
|
|||
template_loc => tempfile_loc,
|
||||
template_loc(nil,'more_') => tempfile_loc(nil,'more_')
|
||||
}
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
['more1', 'more_import'].each { |name| assert_renders_correctly(name, :prefix => 'more_') }
|
||||
end
|
||||
|
||||
|
@ -111,7 +111,7 @@ CSS
|
|||
template_loc => tempfile_loc,
|
||||
template_loc(nil,'more_') => tempfile_loc(nil,'more_')
|
||||
}
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
assert_renders_correctly('more1_with_line_comments', 'more1', :prefix => 'more_')
|
||||
end
|
||||
|
||||
|
@ -157,7 +157,7 @@ CSS
|
|||
|
||||
def test_updating_stylesheets_callback_with_individual_files
|
||||
files = [[template_loc("basic"), tempfile_loc("basic")]]
|
||||
assert_callback(:updating_stylesheets, files) {Sass::Plugin.update_stylesheets(files)}
|
||||
assert_callback(:updating_stylesheets, files) {Haml::Util.silence_haml_warnings{Sass::Plugin.update_stylesheets(files)}}
|
||||
end
|
||||
|
||||
def test_updating_stylesheets_callback_with_never_update
|
||||
|
@ -248,7 +248,7 @@ CSS
|
|||
|
||||
touch 'basic', 'more_'
|
||||
assert_needs_update "import"
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
assert_renders_correctly("import")
|
||||
ensure
|
||||
FileUtils.mv(template_loc("basic", "more_"), template_loc("basic"))
|
||||
|
@ -299,7 +299,7 @@ CSS
|
|||
if block_given?
|
||||
yield
|
||||
else
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
end
|
||||
|
||||
assert run, "Expected #{name} callback to be run with arguments:\n #{expected_args.inspect}"
|
||||
|
@ -322,17 +322,17 @@ CSS
|
|||
if block_given?
|
||||
yield
|
||||
else
|
||||
Sass::Plugin.update_stylesheets
|
||||
update_all_stylesheets!
|
||||
end
|
||||
end
|
||||
|
||||
def assert_callbacks(*args)
|
||||
return Sass::Plugin.update_stylesheets if args.empty?
|
||||
return update_all_stylesheets! if args.empty?
|
||||
assert_callback(*args.pop) {assert_callbacks(*args)}
|
||||
end
|
||||
|
||||
def assert_no_callbacks(*args)
|
||||
return Sass::Plugin.update_stylesheets if args.empty?
|
||||
return update_all_stylesheets! if args.empty?
|
||||
assert_no_callback(*args.pop) {assert_no_callbacks(*args)}
|
||||
end
|
||||
|
||||
|
@ -340,13 +340,19 @@ CSS
|
|||
Sass::Plugin.instance_variable_set('@_sass_callbacks', {})
|
||||
end
|
||||
|
||||
def update_all_stylesheets!
|
||||
Haml::Util.silence_haml_warnings do
|
||||
Sass::Plugin.update_stylesheets
|
||||
end
|
||||
end
|
||||
|
||||
def assert_needs_update(name)
|
||||
assert(Sass::Plugin.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
|
||||
assert(Sass::Plugin::StalenessChecker.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
|
||||
"Expected #{template_loc(name)} to need an update.")
|
||||
end
|
||||
|
||||
def assert_doesnt_need_update(name)
|
||||
assert(!Sass::Plugin.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
|
||||
assert(!Sass::Plugin::StalenessChecker.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
|
||||
"Expected #{template_loc(name)} not to need an update.")
|
||||
end
|
||||
|
||||
|
@ -355,6 +361,7 @@ CSS
|
|||
end
|
||||
|
||||
def reset_mtimes
|
||||
Sass::Plugin::StalenessChecker.dependencies_cache = {}
|
||||
atime = Time.now
|
||||
mtime = Time.now - 5
|
||||
Dir["{#{template_loc},#{tempfile_loc}}/**/*.{css,sass,scss}"].each {|f| File.utime(atime, mtime, f)}
|
||||
|
@ -401,12 +408,6 @@ CSS
|
|||
end
|
||||
end
|
||||
|
||||
module Sass::Plugin
|
||||
class << self
|
||||
public :stylesheet_needs_update?
|
||||
end
|
||||
end
|
||||
|
||||
class Sass::Engine
|
||||
alias_method :old_render, :render
|
||||
|
||||
|
|
0
test/sass/results/warn.css
Normal file
0
test/sass/results/warn.css
Normal file
0
test/sass/results/warn_imported.css
Normal file
0
test/sass/results/warn_imported.css
Normal file
|
@ -143,6 +143,19 @@ SASS
|
|||
assert_equal "Options defined!", resolve("assert_options(round(1.2))")
|
||||
end
|
||||
|
||||
def test_dynamic_url
|
||||
assert_equal "url(foo-bar)", resolve("url($foo)", {}, env('foo' => Sass::Script::String.new("foo-bar")))
|
||||
assert_equal "url(foo-bar baz)", resolve("url($foo $bar)", {}, env('foo' => Sass::Script::String.new("foo-bar"), 'bar' => Sass::Script::String.new("baz")))
|
||||
assert_equal "url(foo baz)", resolve("url(foo $bar)", {}, env('bar' => Sass::Script::String.new("baz")))
|
||||
assert_equal "url(foo bar)", resolve("url(foo bar)")
|
||||
end
|
||||
|
||||
def test_url_with_interpolation
|
||||
assert_equal "url(http://sass-lang.com/images/foo-bar)", resolve("url(http://sass-lang.com/images/\#{foo-bar})")
|
||||
assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve("url('http://sass-lang.com/images/\#{foo-bar}')")
|
||||
assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve('url("http://sass-lang.com/images/#{foo-bar}")')
|
||||
end
|
||||
|
||||
def test_hyphenated_variables
|
||||
assert_equal("a-b", resolve("$a-b", {}, env("a-b" => Sass::Script::String.new("a-b"))))
|
||||
end
|
||||
|
|
|
@ -93,7 +93,7 @@ class ScssRxTest < Test::Unit::TestCase
|
|||
assert_match URI, 'url("foo bar)")'
|
||||
assert_match URI, "url('foo bar)')"
|
||||
assert_match URI, 'url( "foo bar)" )'
|
||||
assert_match URI, "url(!#\$%&**+,-./0123456789~)"
|
||||
assert_match URI, "url(!#\\%&**+,-./0123456789~)"
|
||||
end
|
||||
|
||||
def test_invalid_uri
|
||||
|
|
|
@ -113,6 +113,27 @@ SCSS
|
|||
end
|
||||
end
|
||||
|
||||
def test_warn_directive
|
||||
expected_warning = <<EXPECTATION
|
||||
WARNING: this is a warning
|
||||
on line 2 of test_warn_directive_inline.scss
|
||||
|
||||
WARNING: this is a mixin
|
||||
on line 1 of test_warn_directive_inline.scss, in `foo'
|
||||
from line 3 of test_warn_directive_inline.scss
|
||||
EXPECTATION
|
||||
assert_warning expected_warning do
|
||||
assert_equal <<CSS, render(<<SCSS)
|
||||
bar {
|
||||
c: d; }
|
||||
CSS
|
||||
@mixin foo { @warn "this is a mixin";}
|
||||
@warn "this is a warning";
|
||||
bar {c: d; @include foo;}
|
||||
SCSS
|
||||
end
|
||||
end
|
||||
|
||||
def test_for_directive
|
||||
assert_equal <<CSS, render(<<SCSS)
|
||||
.foo {
|
||||
|
|
|
@ -3,7 +3,8 @@ $preconst: hello
|
|||
=premixin
|
||||
pre-mixin: here
|
||||
|
||||
@import importee.sass, scss_importee, "basic.sass", basic.css, ../results/complex.css, url(partial.sass)
|
||||
@import importee.sass, scss_importee, "basic.sass", basic.css, ../results/complex.css
|
||||
@import url(partial.sass)
|
||||
|
||||
nonimported
|
||||
:myconst $preconst
|
||||
|
|
3
test/sass/templates/warn.sass
Normal file
3
test/sass/templates/warn.sass
Normal file
|
@ -0,0 +1,3 @@
|
|||
@warn "In the main file"
|
||||
@import warn_imported.sass
|
||||
+emits-a-warning
|
4
test/sass/templates/warn_imported.sass
Normal file
4
test/sass/templates/warn_imported.sass
Normal file
|
@ -0,0 +1,4 @@
|
|||
@warn "Imported"
|
||||
|
||||
=emits-a-warning
|
||||
@warn "In an imported mixin"
|
Loading…
Add table
Reference in a new issue