xass 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +11 -0
- data/CONTRIBUTING +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +201 -0
- data/Rakefile +349 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/push +13 -0
- data/bin/sass +13 -0
- data/bin/sass-convert +12 -0
- data/bin/scss +13 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +18 -0
- data/lib/sass/cache_stores/base.rb +88 -0
- data/lib/sass/cache_stores/chain.rb +33 -0
- data/lib/sass/cache_stores/filesystem.rb +64 -0
- data/lib/sass/cache_stores/memory.rb +47 -0
- data/lib/sass/cache_stores/null.rb +25 -0
- data/lib/sass/cache_stores.rb +15 -0
- data/lib/sass/callbacks.rb +66 -0
- data/lib/sass/css.rb +409 -0
- data/lib/sass/engine.rb +930 -0
- data/lib/sass/environment.rb +101 -0
- data/lib/sass/error.rb +201 -0
- data/lib/sass/exec.rb +707 -0
- data/lib/sass/importers/base.rb +139 -0
- data/lib/sass/importers/filesystem.rb +186 -0
- data/lib/sass/importers.rb +22 -0
- data/lib/sass/logger/base.rb +32 -0
- data/lib/sass/logger/log_level.rb +49 -0
- data/lib/sass/logger.rb +15 -0
- data/lib/sass/media.rb +213 -0
- data/lib/sass/plugin/compiler.rb +406 -0
- data/lib/sass/plugin/configuration.rb +123 -0
- data/lib/sass/plugin/generic.rb +15 -0
- data/lib/sass/plugin/merb.rb +48 -0
- data/lib/sass/plugin/rack.rb +60 -0
- data/lib/sass/plugin/rails.rb +47 -0
- data/lib/sass/plugin/staleness_checker.rb +199 -0
- data/lib/sass/plugin.rb +133 -0
- data/lib/sass/railtie.rb +10 -0
- data/lib/sass/repl.rb +57 -0
- data/lib/sass/root.rb +7 -0
- data/lib/sass/script/arg_list.rb +52 -0
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +606 -0
- data/lib/sass/script/css_lexer.rb +29 -0
- data/lib/sass/script/css_parser.rb +31 -0
- data/lib/sass/script/funcall.rb +245 -0
- data/lib/sass/script/functions.rb +1543 -0
- data/lib/sass/script/interpolation.rb +79 -0
- data/lib/sass/script/lexer.rb +345 -0
- data/lib/sass/script/list.rb +85 -0
- data/lib/sass/script/literal.rb +221 -0
- data/lib/sass/script/node.rb +99 -0
- data/lib/sass/script/null.rb +37 -0
- data/lib/sass/script/number.rb +453 -0
- data/lib/sass/script/operation.rb +110 -0
- data/lib/sass/script/parser.rb +502 -0
- data/lib/sass/script/string.rb +51 -0
- data/lib/sass/script/string_interpolation.rb +103 -0
- data/lib/sass/script/unary_operation.rb +69 -0
- data/lib/sass/script/variable.rb +58 -0
- data/lib/sass/script.rb +39 -0
- data/lib/sass/scss/css_parser.rb +36 -0
- data/lib/sass/scss/parser.rb +1180 -0
- data/lib/sass/scss/rx.rb +133 -0
- data/lib/sass/scss/script_lexer.rb +15 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/scss/static_parser.rb +54 -0
- data/lib/sass/scss.rb +16 -0
- data/lib/sass/selector/abstract_sequence.rb +94 -0
- data/lib/sass/selector/comma_sequence.rb +92 -0
- data/lib/sass/selector/sequence.rb +507 -0
- data/lib/sass/selector/simple.rb +119 -0
- data/lib/sass/selector/simple_sequence.rb +215 -0
- data/lib/sass/selector.rb +452 -0
- data/lib/sass/shared.rb +76 -0
- data/lib/sass/supports.rb +229 -0
- data/lib/sass/tree/charset_node.rb +22 -0
- data/lib/sass/tree/comment_node.rb +82 -0
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +60 -0
- data/lib/sass/tree/debug_node.rb +18 -0
- data/lib/sass/tree/directive_node.rb +42 -0
- data/lib/sass/tree/each_node.rb +24 -0
- data/lib/sass/tree/extend_node.rb +36 -0
- data/lib/sass/tree/for_node.rb +36 -0
- data/lib/sass/tree/function_node.rb +34 -0
- data/lib/sass/tree/if_node.rb +52 -0
- data/lib/sass/tree/import_node.rb +75 -0
- data/lib/sass/tree/media_node.rb +58 -0
- data/lib/sass/tree/mixin_def_node.rb +38 -0
- data/lib/sass/tree/mixin_node.rb +39 -0
- data/lib/sass/tree/node.rb +196 -0
- data/lib/sass/tree/prop_node.rb +152 -0
- data/lib/sass/tree/return_node.rb +18 -0
- data/lib/sass/tree/root_node.rb +78 -0
- data/lib/sass/tree/rule_node.rb +132 -0
- data/lib/sass/tree/supports_node.rb +51 -0
- data/lib/sass/tree/trace_node.rb +32 -0
- data/lib/sass/tree/variable_node.rb +30 -0
- data/lib/sass/tree/visitors/base.rb +75 -0
- data/lib/sass/tree/visitors/check_nesting.rb +147 -0
- data/lib/sass/tree/visitors/convert.rb +316 -0
- data/lib/sass/tree/visitors/cssize.rb +241 -0
- data/lib/sass/tree/visitors/deep_copy.rb +102 -0
- data/lib/sass/tree/visitors/extend.rb +68 -0
- data/lib/sass/tree/visitors/perform.rb +446 -0
- data/lib/sass/tree/visitors/set_options.rb +125 -0
- data/lib/sass/tree/visitors/to_css.rb +228 -0
- data/lib/sass/tree/warn_node.rb +18 -0
- data/lib/sass/tree/while_node.rb +18 -0
- data/lib/sass/util/multibyte_string_scanner.rb +155 -0
- data/lib/sass/util/subset_map.rb +109 -0
- data/lib/sass/util/test.rb +10 -0
- data/lib/sass/util.rb +948 -0
- data/lib/sass/version.rb +126 -0
- data/lib/sass.rb +95 -0
- data/rails/init.rb +1 -0
- data/test/Gemfile +3 -0
- data/test/Gemfile.lock +10 -0
- data/test/sass/cache_test.rb +89 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/conversion_test.rb +1760 -0
- data/test/sass/css2sass_test.rb +458 -0
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/engine_test.rb +3244 -0
- data/test/sass/exec_test.rb +86 -0
- data/test/sass/extend_test.rb +1482 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
- data/test/sass/functions_test.rb +1139 -0
- data/test/sass/importer_test.rb +192 -0
- data/test/sass/logger_test.rb +58 -0
- data/test/sass/mock_importer.rb +49 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +564 -0
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/basic.css +9 -0
- data/test/sass/results/cached_import_option.css +3 -0
- data/test/sass/results/compact.css +5 -0
- data/test/sass/results/complex.css +86 -0
- data/test/sass/results/compressed.css +1 -0
- data/test/sass/results/expanded.css +19 -0
- data/test/sass/results/filename_fn.css +3 -0
- data/test/sass/results/if.css +3 -0
- data/test/sass/results/import.css +31 -0
- data/test/sass/results/import_charset.css +5 -0
- data/test/sass/results/import_charset_1_8.css +5 -0
- data/test/sass/results/import_charset_ibm866.css +5 -0
- data/test/sass/results/import_content.css +1 -0
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/mixins.css +95 -0
- data/test/sass/results/multiline.css +24 -0
- data/test/sass/results/nested.css +22 -0
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/script.css +16 -0
- data/test/sass/results/scss_import.css +31 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +3 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/results/warn.css +0 -0
- data/test/sass/results/warn_imported.css +0 -0
- data/test/sass/script_conversion_test.rb +299 -0
- data/test/sass/script_test.rb +622 -0
- data/test/sass/scss/css_test.rb +1100 -0
- data/test/sass/scss/rx_test.rb +156 -0
- data/test/sass/scss/scss_test.rb +2106 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/templates/_cached_import_option_partial.scss +1 -0
- data/test/sass/templates/_double_import_loop2.sass +1 -0
- data/test/sass/templates/_filename_fn_import.scss +11 -0
- data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
- data/test/sass/templates/_imported_charset_utf8.sass +4 -0
- data/test/sass/templates/_imported_content.sass +3 -0
- data/test/sass/templates/_partial.sass +2 -0
- data/test/sass/templates/_same_name_different_partiality.scss +1 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/basic.sass +23 -0
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/bork5.sass +3 -0
- data/test/sass/templates/cached_import_option.scss +3 -0
- data/test/sass/templates/compact.sass +17 -0
- data/test/sass/templates/complex.sass +305 -0
- data/test/sass/templates/compressed.sass +15 -0
- data/test/sass/templates/double_import_loop1.sass +1 -0
- data/test/sass/templates/expanded.sass +17 -0
- data/test/sass/templates/filename_fn.scss +18 -0
- data/test/sass/templates/if.sass +11 -0
- data/test/sass/templates/import.sass +12 -0
- data/test/sass/templates/import_charset.sass +9 -0
- data/test/sass/templates/import_charset_1_8.sass +6 -0
- data/test/sass/templates/import_charset_ibm866.sass +11 -0
- data/test/sass/templates/import_content.sass +4 -0
- data/test/sass/templates/importee.less +2 -0
- data/test/sass/templates/importee.sass +19 -0
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +76 -0
- data/test/sass/templates/multiline.sass +20 -0
- data/test/sass/templates/nested.sass +25 -0
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_import.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/same_name_different_ext.sass +2 -0
- data/test/sass/templates/same_name_different_ext.scss +1 -0
- data/test/sass/templates/same_name_different_partiality.scss +1 -0
- data/test/sass/templates/script.sass +101 -0
- data/test/sass/templates/scss_import.scss +11 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/single_import_loop.sass +1 -0
- data/test/sass/templates/subdir/import_up1.scss +1 -0
- data/test/sass/templates/subdir/import_up2.scss +1 -0
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- data/test/sass/templates/units.sass +11 -0
- data/test/sass/templates/warn.sass +3 -0
- data/test/sass/templates/warn_imported.sass +4 -0
- data/test/sass/test_helper.rb +8 -0
- data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
- data/test/sass/util/subset_map_test.rb +91 -0
- data/test/sass/util_test.rb +382 -0
- data/test/test_helper.rb +80 -0
- metadata +354 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module Sass
|
|
2
|
+
module Importers
|
|
3
|
+
# The abstract base class for Sass importers.
|
|
4
|
+
# All importers should inherit from this.
|
|
5
|
+
#
|
|
6
|
+
# At the most basic level, an importer is given a string
|
|
7
|
+
# and must return a {Sass::Engine} containing some Sass code.
|
|
8
|
+
# This string can be interpreted however the importer wants;
|
|
9
|
+
# however, subclasses are encouraged to use the URI format
|
|
10
|
+
# for pathnames.
|
|
11
|
+
#
|
|
12
|
+
# Importers that have some notion of "relative imports"
|
|
13
|
+
# should take a single load path in their constructor,
|
|
14
|
+
# and interpret paths as relative to that.
|
|
15
|
+
# They should also implement the \{#find\_relative} method.
|
|
16
|
+
#
|
|
17
|
+
# Importers should be serializable via `Marshal.dump`.
|
|
18
|
+
# In addition to the standard `_dump` and `_load` methods,
|
|
19
|
+
# importers can define `_before_dump`, `_after_dump`, `_around_dump`,
|
|
20
|
+
# and `_after_load` methods as per {Sass::Util#dump} and {Sass::Util#load}.
|
|
21
|
+
#
|
|
22
|
+
# @abstract
|
|
23
|
+
class Base
|
|
24
|
+
|
|
25
|
+
# Find a Sass file relative to another file.
|
|
26
|
+
# Importers without a notion of "relative paths"
|
|
27
|
+
# should just return nil here.
|
|
28
|
+
#
|
|
29
|
+
# If the importer does have a notion of "relative paths",
|
|
30
|
+
# it should ignore its load path during this method.
|
|
31
|
+
#
|
|
32
|
+
# See \{#find} for important information on how this method should behave.
|
|
33
|
+
#
|
|
34
|
+
# The `:filename` option passed to the returned {Sass::Engine}
|
|
35
|
+
# should be of a format that could be passed to \{#find}.
|
|
36
|
+
#
|
|
37
|
+
# @param uri [String] The URI to import. This is not necessarily relative,
|
|
38
|
+
# but this method should only return true if it is.
|
|
39
|
+
# @param base [String] The base filename. If `uri` is relative,
|
|
40
|
+
# it should be interpreted as relative to `base`.
|
|
41
|
+
# `base` is guaranteed to be in a format importable by this importer.
|
|
42
|
+
# @param options [{Symbol => Object}] Options for the Sass file
|
|
43
|
+
# containing the `@import` that's currently being resolved.
|
|
44
|
+
# @return [Sass::Engine, nil] An Engine containing the imported file,
|
|
45
|
+
# or nil if it couldn't be found or was in the wrong format.
|
|
46
|
+
def find_relative(uri, base, options)
|
|
47
|
+
Sass::Util.abstract(self)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Find a Sass file, if it exists.
|
|
51
|
+
#
|
|
52
|
+
# This is the primary entry point of the Importer.
|
|
53
|
+
# It corresponds directly to an `@import` statement in Sass.
|
|
54
|
+
# It should do three basic things:
|
|
55
|
+
#
|
|
56
|
+
# * Determine if the URI is in this importer's format.
|
|
57
|
+
# If not, return nil.
|
|
58
|
+
# * Determine if the file indicated by the URI actually exists and is readable.
|
|
59
|
+
# If not, return nil.
|
|
60
|
+
# * Read the file and place the contents in a {Sass::Engine}.
|
|
61
|
+
# Return that engine.
|
|
62
|
+
#
|
|
63
|
+
# If this importer's format allows for file extensions,
|
|
64
|
+
# it should treat them the same way as the default {Filesystem} importer.
|
|
65
|
+
# If the URI explicitly has a `.sass` or `.scss` filename,
|
|
66
|
+
# the importer should look for that exact file
|
|
67
|
+
# and import it as the syntax indicated.
|
|
68
|
+
# If it doesn't exist, the importer should return nil.
|
|
69
|
+
#
|
|
70
|
+
# If the URI doesn't have either of these extensions,
|
|
71
|
+
# the importer should look for files with the extensions.
|
|
72
|
+
# If no such files exist, it should return nil.
|
|
73
|
+
#
|
|
74
|
+
# The {Sass::Engine} to be returned should be passed `options`,
|
|
75
|
+
# with a few modifications. `:syntax` should be set appropriately,
|
|
76
|
+
# `:filename` should be set to `uri`,
|
|
77
|
+
# and `:importer` should be set to this importer.
|
|
78
|
+
#
|
|
79
|
+
# @param uri [String] The URI to import.
|
|
80
|
+
# @param options [{Symbol => Object}] Options for the Sass file
|
|
81
|
+
# containing the `@import` that's currently being resolved.
|
|
82
|
+
# This is safe for subclasses to modify destructively.
|
|
83
|
+
# Callers should only pass in a value they don't mind being destructively modified.
|
|
84
|
+
# @return [Sass::Engine, nil] An Engine containing the imported file,
|
|
85
|
+
# or nil if it couldn't be found or was in the wrong format.
|
|
86
|
+
def find(uri, options)
|
|
87
|
+
Sass::Util.abstract(self)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Returns the time the given Sass file was last modified.
|
|
91
|
+
#
|
|
92
|
+
# If the given file has been deleted or the time can't be accessed
|
|
93
|
+
# for some other reason, this should return nil.
|
|
94
|
+
#
|
|
95
|
+
# @param uri [String] The URI of the file to check.
|
|
96
|
+
# Comes from a `:filename` option set on an engine returned by this importer.
|
|
97
|
+
# @param options [{Symbol => Objet}] Options for the Sass file
|
|
98
|
+
# containing the `@import` currently being checked.
|
|
99
|
+
# @return [Time, nil]
|
|
100
|
+
def mtime(uri, options)
|
|
101
|
+
Sass::Util.abstract(self)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Get the cache key pair for the given Sass URI.
|
|
105
|
+
# The URI need not be checked for validity.
|
|
106
|
+
#
|
|
107
|
+
# The only strict requirement is that the returned pair of strings
|
|
108
|
+
# uniquely identify the file at the given URI.
|
|
109
|
+
# However, the first component generally corresponds roughly to the directory,
|
|
110
|
+
# and the second to the basename, of the URI.
|
|
111
|
+
#
|
|
112
|
+
# Note that keys must be unique *across importers*.
|
|
113
|
+
# Thus it's probably a good idea to include the importer name
|
|
114
|
+
# at the beginning of the first component.
|
|
115
|
+
#
|
|
116
|
+
# @param uri [String] A URI known to be valid for this importer.
|
|
117
|
+
# @param options [{Symbol => Object}] Options for the Sass file
|
|
118
|
+
# containing the `@import` currently being checked.
|
|
119
|
+
# @return [(String, String)] The key pair which uniquely identifies
|
|
120
|
+
# the file at the given URI.
|
|
121
|
+
def key(uri, options)
|
|
122
|
+
Sass::Util.abstract(self)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# A string representation of the importer.
|
|
126
|
+
# Should be overridden by subclasses.
|
|
127
|
+
#
|
|
128
|
+
# This is used to help debugging,
|
|
129
|
+
# and should usually just show the load path encapsulated by this importer.
|
|
130
|
+
#
|
|
131
|
+
# @return [String]
|
|
132
|
+
def to_s
|
|
133
|
+
Sass::Util.abstract(self)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
module Sass
|
|
5
|
+
module Importers
|
|
6
|
+
# The default importer, used for any strings found in the load path.
|
|
7
|
+
# Simply loads Sass files from the filesystem using the default logic.
|
|
8
|
+
class Filesystem < Base
|
|
9
|
+
|
|
10
|
+
attr_accessor :root
|
|
11
|
+
|
|
12
|
+
# Creates a new filesystem importer that imports files relative to a given path.
|
|
13
|
+
#
|
|
14
|
+
# @param root [String] The root path.
|
|
15
|
+
# This importer will import files relative to this path.
|
|
16
|
+
def initialize(root)
|
|
17
|
+
@root = File.expand_path(root)
|
|
18
|
+
@same_name_warnings = Set.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @see Base#find_relative
|
|
22
|
+
def find_relative(name, base, options)
|
|
23
|
+
_find(File.dirname(base), name, options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @see Base#find
|
|
27
|
+
def find(name, options)
|
|
28
|
+
_find(@root, name, options)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @see Base#mtime
|
|
32
|
+
def mtime(name, options)
|
|
33
|
+
file, _ = Sass::Util.destructure(find_real_file(@root, name, options))
|
|
34
|
+
File.mtime(file) if file
|
|
35
|
+
rescue Errno::ENOENT
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @see Base#key
|
|
40
|
+
def key(name, options)
|
|
41
|
+
[self.class.name + ":" + File.dirname(File.expand_path(name)),
|
|
42
|
+
File.basename(name)]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @see Base#to_s
|
|
46
|
+
def to_s
|
|
47
|
+
@root
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def hash
|
|
51
|
+
@root.hash
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def eql?(other)
|
|
55
|
+
root.eql?(other.root)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
protected
|
|
59
|
+
|
|
60
|
+
# If a full uri is passed, this removes the root from it
|
|
61
|
+
# otherwise returns the name unchanged
|
|
62
|
+
def remove_root(name)
|
|
63
|
+
if name.index(@root + "/") == 0
|
|
64
|
+
name[(@root.length + 1)..-1]
|
|
65
|
+
else
|
|
66
|
+
name
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# A hash from file extensions to the syntaxes for those extensions.
|
|
71
|
+
# The syntaxes must be `:sass` or `:scss`.
|
|
72
|
+
#
|
|
73
|
+
# This can be overridden by subclasses that want normal filesystem importing
|
|
74
|
+
# with unusual extensions.
|
|
75
|
+
#
|
|
76
|
+
# @return [{String => Symbol}]
|
|
77
|
+
def extensions
|
|
78
|
+
{'sass' => :sass, 'scss' => :scss}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Given an `@import`ed path, returns an array of possible
|
|
82
|
+
# on-disk filenames and their corresponding syntaxes for that path.
|
|
83
|
+
#
|
|
84
|
+
# @param name [String] The filename.
|
|
85
|
+
# @return [Array(String, Symbol)] An array of pairs.
|
|
86
|
+
# The first element of each pair is a filename to look for;
|
|
87
|
+
# the second element is the syntax that file would be in (`:sass` or `:scss`).
|
|
88
|
+
def possible_files(name)
|
|
89
|
+
name = escape_glob_characters(name)
|
|
90
|
+
dirname, basename, extname = split(name)
|
|
91
|
+
sorted_exts = extensions.sort
|
|
92
|
+
syntax = extensions[extname]
|
|
93
|
+
|
|
94
|
+
if syntax
|
|
95
|
+
ret = [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]]
|
|
96
|
+
else
|
|
97
|
+
ret = sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# JRuby chokes when trying to import files from JARs when the path starts with './'.
|
|
101
|
+
ret.map {|f, s| [f.sub(%r{^\./}, ''), s]}
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def escape_glob_characters(name)
|
|
105
|
+
name.gsub(/[\*\[\]\{\}\?]/) do |char|
|
|
106
|
+
"\\#{char}"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
|
|
111
|
+
# Given a base directory and an `@import`ed name,
|
|
112
|
+
# finds an existant file that matches the name.
|
|
113
|
+
#
|
|
114
|
+
# @param dir [String] The directory relative to which to search.
|
|
115
|
+
# @param name [String] The filename to search for.
|
|
116
|
+
# @return [(String, Symbol)] A filename-syntax pair.
|
|
117
|
+
def find_real_file(dir, name, options)
|
|
118
|
+
# on windows 'dir' can be in native File::ALT_SEPARATOR form
|
|
119
|
+
dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
|
|
120
|
+
|
|
121
|
+
found = possible_files(remove_root(name)).map do |f, s|
|
|
122
|
+
path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{escape_glob_characters(dir)}/#{f}"
|
|
123
|
+
Dir[path].map do |full_path|
|
|
124
|
+
full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
|
|
125
|
+
[Pathname.new(full_path).cleanpath.to_s, s]
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
found = Sass::Util.flatten(found, 1)
|
|
129
|
+
return if found.empty?
|
|
130
|
+
|
|
131
|
+
if found.size > 1 && !@same_name_warnings.include?(found.first.first)
|
|
132
|
+
found.each {|(f, _)| @same_name_warnings << f}
|
|
133
|
+
relative_to = Pathname.new(dir)
|
|
134
|
+
if options[:_line]
|
|
135
|
+
# If _line exists, we're here due to an actual import in an
|
|
136
|
+
# import_node and we want to print a warning for a user writing an
|
|
137
|
+
# ambiguous import.
|
|
138
|
+
candidates = found.map {|(f, _)| " " + Pathname.new(f).relative_path_from(relative_to).to_s}.join("\n")
|
|
139
|
+
Sass::Util.sass_warn <<WARNING
|
|
140
|
+
WARNING: On line #{options[:_line]}#{" of #{options[:filename]}" if options[:filename]}:
|
|
141
|
+
It's not clear which file to import for '@import "#{name}"'.
|
|
142
|
+
Candidates:
|
|
143
|
+
#{candidates}
|
|
144
|
+
For now I'll choose #{File.basename found.first.first}.
|
|
145
|
+
This will be an error in future versions of Sass.
|
|
146
|
+
WARNING
|
|
147
|
+
else
|
|
148
|
+
# Otherwise, we're here via StalenessChecker, and we want to print a
|
|
149
|
+
# warning for a user running `sass --watch` with two ambiguous files.
|
|
150
|
+
candidates = found.map {|(f, _)| " " + File.basename(f)}.join("\n")
|
|
151
|
+
Sass::Util.sass_warn <<WARNING
|
|
152
|
+
WARNING: In #{File.dirname(name)}:
|
|
153
|
+
There are multiple files that match the name "#{File.basename(name)}":
|
|
154
|
+
#{candidates}
|
|
155
|
+
WARNING
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
found.first
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Splits a filename into three parts, a directory part, a basename, and an extension
|
|
162
|
+
# Only the known extensions returned from the extensions method will be recognized as such.
|
|
163
|
+
def split(name)
|
|
164
|
+
extension = nil
|
|
165
|
+
dirname, basename = File.dirname(name), File.basename(name)
|
|
166
|
+
if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
|
|
167
|
+
basename = $1
|
|
168
|
+
extension = $2
|
|
169
|
+
end
|
|
170
|
+
[dirname, basename, extension]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
private
|
|
174
|
+
|
|
175
|
+
def _find(dir, name, options)
|
|
176
|
+
full_filename, syntax = Sass::Util.destructure(find_real_file(dir, name, options))
|
|
177
|
+
return unless full_filename && File.readable?(full_filename)
|
|
178
|
+
|
|
179
|
+
options[:syntax] = syntax
|
|
180
|
+
options[:filename] = full_filename
|
|
181
|
+
options[:importer] = self
|
|
182
|
+
Sass::Engine.new(File.read(full_filename), options)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Sass
|
|
2
|
+
# Sass importers are in charge of taking paths passed to `@import`
|
|
3
|
+
# and finding the appropriate Sass code for those paths.
|
|
4
|
+
# By default, this code is always loaded from the filesystem,
|
|
5
|
+
# but importers could be added to load from a database or over HTTP.
|
|
6
|
+
#
|
|
7
|
+
# Each importer is in charge of a single load path
|
|
8
|
+
# (or whatever the corresponding notion is for the backend).
|
|
9
|
+
# Importers can be placed in the {file:SASS_REFERENCE.md#load_paths-option `:load_paths` array}
|
|
10
|
+
# alongside normal filesystem paths.
|
|
11
|
+
#
|
|
12
|
+
# When resolving an `@import`, Sass will go through the load paths
|
|
13
|
+
# looking for an importer that successfully imports the path.
|
|
14
|
+
# Once one is found, the imported file is used.
|
|
15
|
+
#
|
|
16
|
+
# User-created importers must inherit from {Importers::Base}.
|
|
17
|
+
module Importers
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'sass/importers/base'
|
|
22
|
+
require 'sass/importers/filesystem'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'sass/logger/log_level'
|
|
2
|
+
|
|
3
|
+
class Sass::Logger::Base
|
|
4
|
+
|
|
5
|
+
include Sass::Logger::LogLevel
|
|
6
|
+
|
|
7
|
+
attr_accessor :log_level
|
|
8
|
+
attr_accessor :disabled
|
|
9
|
+
|
|
10
|
+
log_level :trace
|
|
11
|
+
log_level :debug
|
|
12
|
+
log_level :info
|
|
13
|
+
log_level :warn
|
|
14
|
+
log_level :error
|
|
15
|
+
|
|
16
|
+
def initialize(log_level = :debug)
|
|
17
|
+
self.log_level = log_level
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def logging_level?(level)
|
|
21
|
+
!disabled && self.class.log_level?(level, log_level)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def log(level, message)
|
|
25
|
+
self._log(level, message) if logging_level?(level)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def _log(level, message)
|
|
29
|
+
Kernel::warn(message)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Sass
|
|
2
|
+
module Logger
|
|
3
|
+
module LogLevel
|
|
4
|
+
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def inherited(subclass)
|
|
11
|
+
subclass.log_levels = subclass.superclass.log_levels.dup
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def log_levels
|
|
15
|
+
@log_levels ||= {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def log_levels=(levels)
|
|
19
|
+
@log_levels = levels
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def log_level?(level, min_level)
|
|
23
|
+
log_levels[level] >= log_levels[min_level]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def log_level(name, options = {})
|
|
27
|
+
if options[:prepend]
|
|
28
|
+
level = log_levels.values.min
|
|
29
|
+
level = level.nil? ? 0 : level - 1
|
|
30
|
+
else
|
|
31
|
+
level = log_levels.values.max
|
|
32
|
+
level = level.nil? ? 0 : level + 1
|
|
33
|
+
end
|
|
34
|
+
log_levels.update(name => level)
|
|
35
|
+
define_logger(name)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def define_logger(name, options = {})
|
|
39
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
40
|
+
def #{name}(message)
|
|
41
|
+
#{options.fetch(:to, :log)}(#{name.inspect}, message)
|
|
42
|
+
end
|
|
43
|
+
RUBY
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/sass/logger.rb
ADDED
data/lib/sass/media.rb
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# A namespace for the `@media` query parse tree.
|
|
2
|
+
module Sass::Media
|
|
3
|
+
# A comma-separated list of queries.
|
|
4
|
+
#
|
|
5
|
+
# media_query [ ',' S* media_query ]*
|
|
6
|
+
class QueryList
|
|
7
|
+
# The queries contained in this list.
|
|
8
|
+
#
|
|
9
|
+
# @return [Array<Query>]
|
|
10
|
+
attr_accessor :queries
|
|
11
|
+
|
|
12
|
+
# @param queries [Array<Query>] See \{#queries}
|
|
13
|
+
def initialize(queries)
|
|
14
|
+
@queries = queries
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Merges this query list with another. The returned query list
|
|
18
|
+
# queries for the intersection between the two inputs.
|
|
19
|
+
#
|
|
20
|
+
# Both query lists should be resolved.
|
|
21
|
+
#
|
|
22
|
+
# @param other [QueryList]
|
|
23
|
+
# @return [QueryList?] The merged list, or nil if there is no intersection.
|
|
24
|
+
def merge(other)
|
|
25
|
+
new_queries = queries.map {|q1| other.queries.map {|q2| q1.merge(q2)}}.flatten.compact
|
|
26
|
+
return if new_queries.empty?
|
|
27
|
+
QueryList.new(new_queries)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the CSS for the media query list.
|
|
31
|
+
#
|
|
32
|
+
# @return [String]
|
|
33
|
+
def to_css
|
|
34
|
+
queries.map {|q| q.to_css}.join(', ')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns the Sass/SCSS code for the media query list.
|
|
38
|
+
#
|
|
39
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
40
|
+
# @return [String]
|
|
41
|
+
def to_src(options)
|
|
42
|
+
queries.map {|q| q.to_src(options)}.join(', ')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns a representation of the query as an array of strings and
|
|
46
|
+
# potentially {Sass::Script::Node}s (if there's interpolation in it). When
|
|
47
|
+
# the interpolation is resolved and the strings are joined together, this
|
|
48
|
+
# will be the string representation of this query.
|
|
49
|
+
#
|
|
50
|
+
# @return [Array<String, Sass::Script::Node>]
|
|
51
|
+
def to_a
|
|
52
|
+
Sass::Util.intersperse(queries.map {|q| q.to_a}, ', ').flatten
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns a deep copy of this query list and all its children.
|
|
56
|
+
#
|
|
57
|
+
# @return [QueryList]
|
|
58
|
+
def deep_copy
|
|
59
|
+
QueryList.new(queries.map {|q| q.deep_copy})
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# A single media query.
|
|
64
|
+
#
|
|
65
|
+
# [ [ONLY | NOT]? S* media_type S* | expression ] [ AND S* expression ]*
|
|
66
|
+
class Query
|
|
67
|
+
# The modifier for the query.
|
|
68
|
+
#
|
|
69
|
+
# When parsed as Sass code, this contains strings and SassScript nodes. When
|
|
70
|
+
# parsed as CSS, it contains a single string (accessible via
|
|
71
|
+
# \{#resolved_modifier}).
|
|
72
|
+
#
|
|
73
|
+
# @return [Array<String, Sass::Script::Node>]
|
|
74
|
+
attr_accessor :modifier
|
|
75
|
+
|
|
76
|
+
# The type of the query (e.g. `"screen"` or `"print"`).
|
|
77
|
+
#
|
|
78
|
+
# When parsed as Sass code, this contains strings and SassScript nodes. When
|
|
79
|
+
# parsed as CSS, it contains a single string (accessible via
|
|
80
|
+
# \{#resolved_type}).
|
|
81
|
+
#
|
|
82
|
+
# @return [Array<String, Sass::Script::Node>]
|
|
83
|
+
attr_accessor :type
|
|
84
|
+
|
|
85
|
+
# The trailing expressions in the query.
|
|
86
|
+
#
|
|
87
|
+
# When parsed as Sass code, each expression contains strings and SassScript
|
|
88
|
+
# nodes. When parsed as CSS, each one contains a single string.
|
|
89
|
+
#
|
|
90
|
+
# @return [Array<Array<String, Sass::Script::Node>>]
|
|
91
|
+
attr_accessor :expressions
|
|
92
|
+
|
|
93
|
+
# @param modifier [Array<String, Sass::Script::Node>] See \{#modifier}
|
|
94
|
+
# @param type [Array<String, Sass::Script::Node>] See \{#type}
|
|
95
|
+
# @param expressions [Array<Array<String, Sass::Script::Node>>] See \{#expressions}
|
|
96
|
+
def initialize(modifier, type, expressions)
|
|
97
|
+
@modifier = modifier
|
|
98
|
+
@type = type
|
|
99
|
+
@expressions = expressions
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# See \{#modifier}.
|
|
103
|
+
# @return [String]
|
|
104
|
+
def resolved_modifier
|
|
105
|
+
# modifier should contain only a single string
|
|
106
|
+
modifier.first || ''
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# See \{#type}.
|
|
110
|
+
# @return [String]
|
|
111
|
+
def resolved_type
|
|
112
|
+
# type should contain only a single string
|
|
113
|
+
type.first || ''
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Merges this query with another. The returned query queries for
|
|
117
|
+
# the intersection between the two inputs.
|
|
118
|
+
#
|
|
119
|
+
# Both queries should be resolved.
|
|
120
|
+
#
|
|
121
|
+
# @param other [Query]
|
|
122
|
+
# @return [Query?] The merged query, or nil if there is no intersection.
|
|
123
|
+
def merge(other)
|
|
124
|
+
m1, t1 = resolved_modifier.downcase, resolved_type.downcase
|
|
125
|
+
m2, t2 = other.resolved_modifier.downcase, other.resolved_type.downcase
|
|
126
|
+
t1 = t2 if t1.empty?
|
|
127
|
+
t2 = t1 if t2.empty?
|
|
128
|
+
if ((m1 == 'not') ^ (m2 == 'not'))
|
|
129
|
+
return if t1 == t2
|
|
130
|
+
type = m1 == 'not' ? t2 : t1
|
|
131
|
+
mod = m1 == 'not' ? m2 : m1
|
|
132
|
+
elsif m1 == 'not' && m2 == 'not'
|
|
133
|
+
# CSS has no way of representing "neither screen nor print"
|
|
134
|
+
return unless t1 == t2
|
|
135
|
+
type = t1
|
|
136
|
+
mod = 'not'
|
|
137
|
+
elsif t1 != t2
|
|
138
|
+
return
|
|
139
|
+
else # t1 == t2, neither m1 nor m2 are "not"
|
|
140
|
+
type = t1
|
|
141
|
+
mod = m1.empty? ? m2 : m1
|
|
142
|
+
end
|
|
143
|
+
return Query.new([mod], [type], other.expressions + expressions)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Returns the CSS for the media query.
|
|
147
|
+
#
|
|
148
|
+
# @return [String]
|
|
149
|
+
def to_css
|
|
150
|
+
css = ''
|
|
151
|
+
css << resolved_modifier
|
|
152
|
+
css << ' ' unless resolved_modifier.empty?
|
|
153
|
+
css << resolved_type
|
|
154
|
+
css << ' and ' unless resolved_type.empty? || expressions.empty?
|
|
155
|
+
css << expressions.map do |e|
|
|
156
|
+
# It's possible for there to be script nodes in Expressions even when
|
|
157
|
+
# we're converting to CSS in the case where we parsed the document as
|
|
158
|
+
# CSS originally (as in css_test.rb).
|
|
159
|
+
e.map {|c| c.is_a?(Sass::Script::Node) ? c.to_sass : c.to_s}.join
|
|
160
|
+
end.join(' and ')
|
|
161
|
+
css
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Returns the Sass/SCSS code for the media query.
|
|
165
|
+
#
|
|
166
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
167
|
+
# @return [String]
|
|
168
|
+
def to_src(options)
|
|
169
|
+
src = ''
|
|
170
|
+
src << Sass::Media._interp_to_src(modifier, options)
|
|
171
|
+
src << ' ' unless modifier.empty?
|
|
172
|
+
src << Sass::Media._interp_to_src(type, options)
|
|
173
|
+
src << ' and ' unless type.empty? || expressions.empty?
|
|
174
|
+
src << expressions.map do |e|
|
|
175
|
+
Sass::Media._interp_to_src(e, options)
|
|
176
|
+
end.join(' and ')
|
|
177
|
+
src
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# @see \{MediaQuery#to\_a}
|
|
181
|
+
def to_a
|
|
182
|
+
res = []
|
|
183
|
+
res += modifier
|
|
184
|
+
res << ' ' unless modifier.empty?
|
|
185
|
+
res += type
|
|
186
|
+
res << ' and ' unless type.empty? || expressions.empty?
|
|
187
|
+
res += Sass::Util.intersperse(expressions, ' and ').flatten
|
|
188
|
+
res
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Returns a deep copy of this query and all its children.
|
|
192
|
+
#
|
|
193
|
+
# @return [Query]
|
|
194
|
+
def deep_copy
|
|
195
|
+
Query.new(
|
|
196
|
+
modifier.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
|
|
197
|
+
type.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
|
|
198
|
+
expressions.map {|e| e.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}})
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Converts an interpolation array to source.
|
|
203
|
+
#
|
|
204
|
+
# @param [Array<String, Sass::Script::Node>] The interpolation array to convert.
|
|
205
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
206
|
+
# @return [String]
|
|
207
|
+
def self._interp_to_src(interp, options)
|
|
208
|
+
interp.map do |r|
|
|
209
|
+
next r if r.is_a?(String)
|
|
210
|
+
"\#{#{r.to_sass(options)}}"
|
|
211
|
+
end.join
|
|
212
|
+
end
|
|
213
|
+
end
|