SassyJSON 1.1.1 → 1.1.2

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +18 -18
  3. data/README.md +112 -112
  4. data/lib/JsonImporter.rb +228 -228
  5. data/lib/SassyJSON.rb +12 -14
  6. data/stylesheets/SassyJSON.scss +5 -5
  7. data/stylesheets/decode/api/_json.scss +26 -26
  8. data/stylesheets/decode/decode.scss +28 -28
  9. data/stylesheets/decode/helpers/all/_throw.scss +11 -11
  10. data/stylesheets/decode/helpers/all/_value.scss +49 -49
  11. data/stylesheets/decode/helpers/color/_color.scss +50 -50
  12. data/stylesheets/decode/helpers/color/_get-color-value.scss +22 -22
  13. data/stylesheets/decode/helpers/color/_hex-to-dec.scss +19 -19
  14. data/stylesheets/decode/helpers/color/_hex.scss +39 -39
  15. data/stylesheets/decode/helpers/color/_hsl.scss +46 -46
  16. data/stylesheets/decode/helpers/color/_rgb.scss +46 -46
  17. data/stylesheets/decode/helpers/map/_consume.scss +33 -33
  18. data/stylesheets/decode/helpers/number/_find-digits.scss +39 -39
  19. data/stylesheets/decode/helpers/number/_find-exponent.scss +51 -51
  20. data/stylesheets/decode/helpers/number/_find-integer.scss +37 -37
  21. data/stylesheets/decode/helpers/number/_pow.scss +22 -22
  22. data/stylesheets/decode/helpers/string/_find-ending-quote.scss +57 -57
  23. data/stylesheets/decode/helpers/string/_length.scss +42 -42
  24. data/stylesheets/decode/helpers/string/_strip-token.scss +16 -16
  25. data/stylesheets/decode/types/_bool.scss +40 -40
  26. data/stylesheets/decode/types/_list.scss +54 -54
  27. data/stylesheets/decode/types/_map.scss +78 -78
  28. data/stylesheets/decode/types/_null.scss +19 -19
  29. data/stylesheets/decode/types/_number.scss +63 -63
  30. data/stylesheets/decode/types/_string.scss +41 -41
  31. data/stylesheets/encode/api/_json.scss +17 -17
  32. data/stylesheets/encode/encode.scss +17 -17
  33. data/stylesheets/encode/helpers/_quote.scss +9 -9
  34. data/stylesheets/encode/mixins/_json.scss +36 -36
  35. data/stylesheets/encode/types/_bool.scss +9 -9
  36. data/stylesheets/encode/types/_color.scss +9 -9
  37. data/stylesheets/encode/types/_list.scss +13 -13
  38. data/stylesheets/encode/types/_map.scss +13 -13
  39. data/stylesheets/encode/types/_null.scss +9 -9
  40. data/stylesheets/encode/types/_number.scss +9 -9
  41. data/stylesheets/encode/types/_string.scss +9 -9
  42. metadata +8 -10
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c767cc7d4815295989917dffc2c37a090b40726
4
+ data.tar.gz: dd04982601e4a24085741e479024a7fe0afdff15
5
+ SHA512:
6
+ metadata.gz: 6e98df2f7645642b6d9a650e84bf54a7a8cb6881725e3ab6d46cb5ad88153c64e5eb2614834267609ced30a6d97be6f06173fefc80f1bb1b5d15d00a91e14318
7
+ data.tar.gz: c5b1004ba0f86565ee51c367730d23a8cd7cff70188907de4234bbdb2ee4e1ad606d49195ec923902d5bdd22bb35fd43f51bee11678cd9264db0bba07e977744
data/CHANGELOG.md CHANGED
@@ -1,18 +1,18 @@
1
- # Changelog
2
-
3
- * `1.1.1`: fixing a minor issue with the gem
4
- * `1.1.0`: adding the ability to import a JSON file
5
- * `1.0.11`: fixing an issue with scientific number parsing
6
- * `1.0.10`: improving number and string helpers
7
- * `1.0.9`: fixing a bug in `_find-exponent`
8
- * `1.0.8`: fixing a major issue in Ruby Gem
9
- * `1.0.7`: minor fixes and stable Ruby Gem
10
- * `1.0.6`: released a Ruby Gem
11
- * `1.0.5`: improved the encoding mixin
12
- * `1.0.4`: fixed an error in map parsing
13
- * `1.0.3`: slightly edited the mixin to dump JSON to CSS
14
- * `1.0.2`: fixed an issue with string parsing
15
- * `1.0.1`: fixed an issue with alpha color parsing
16
- * `1.0.0`: Stable API. `json-encode` and `json-decode`
17
- * `0.0.2`: added `json-decode` and test
18
- * `0.0.1`: initial commit
1
+ # Changelog
2
+
3
+ * `1.1.1`: fixing a minor issue with the gem
4
+ * `1.1.0`: adding the ability to import a JSON file
5
+ * `1.0.11`: fixing an issue with scientific number parsing
6
+ * `1.0.10`: improving number and string helpers
7
+ * `1.0.9`: fixing a bug in `_find-exponent`
8
+ * `1.0.8`: fixing a major issue in Ruby Gem
9
+ * `1.0.7`: minor fixes and stable Ruby Gem
10
+ * `1.0.6`: released a Ruby Gem
11
+ * `1.0.5`: improved the encoding mixin
12
+ * `1.0.4`: fixed an error in map parsing
13
+ * `1.0.3`: slightly edited the mixin to dump JSON to CSS
14
+ * `1.0.2`: fixed an issue with string parsing
15
+ * `1.0.1`: fixed an issue with alpha color parsing
16
+ * `1.0.0`: Stable API. `json-encode` and `json-decode`
17
+ * `0.0.2`: added `json-decode` and test
18
+ * `0.0.1`: initial commit
data/README.md CHANGED
@@ -1,112 +1,112 @@
1
- # SassyJSON [![NPM version](https://badge.fury.io/js/sassyjson.png)](http://badge.fury.io/js/sassyjson) [![Gem Version](https://badge.fury.io/rb/SassyJSON.png)](http://badge.fury.io/rb/SassyJSON) [![Build Status](https://travis-ci.org/HugoGiraudel/SassyJSON.png?branch=master)](https://travis-ci.org/HugoGiraudel/SassyJSON)
2
-
3
- SassyJSON is a Sass-powered API for JSON. It provides you the classic `json-encode` and `json-decode` directly from your Sass files. We'll leave you the only judges of the point of this.
4
-
5
- ## Install
6
-
7
- SassyJSON is available on [npm](https://npmjs.org/) or as a [Ruby Gem](http://rubygems.org/gems/SassyJSON).
8
-
9
- ### Git
10
-
11
- ``` git
12
- git clone https://github.com/HugoGiraudel/SassyJSON.git && cd SassyJSON
13
- ```
14
-
15
- ### npm
16
-
17
- ``` bash
18
- npm install sassyjson --save-dev
19
- ```
20
-
21
- ### Compass extension
22
-
23
- 1. `gem install SassyJSON`
24
- 2. Add `require 'SassyJSON'` to your `config.rb`
25
- 3. Import it in your stylesheets with `@import 'SassyJSON'`
26
-
27
- ### Sass
28
-
29
- If you only want to play around the code without cloning the repo or using npm, you can find a [single file](https://github.com/HugoGiraudel/SassyJSON/blob/master/dist/_SassyJSON.scss) containing the whole API in the [dist](https://github.com/HugoGiraudel/SassyJSON/tree/master/dist) folder.
30
-
31
- Also, SassyJSON is available at [Sassmeister](http://sassmeister.com/).
32
-
33
- ## Example
34
-
35
- ### Encoding Sass to JSON
36
-
37
- #### Sass
38
-
39
- ``` scss
40
- $map: ((a: (1 2 ( b : 1 )), b: ( #444444, false, ( a: 1, b: test ) ), c: (2 3 4 string)));
41
-
42
- @include json-encode($map);
43
- ```
44
-
45
- #### CSS
46
-
47
- ``` css
48
- /*! json-encode: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}' */
49
-
50
- body::before {
51
- content: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
52
- }
53
-
54
- head {
55
- font-family: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
56
- }
57
-
58
- @media -json-encode {
59
- json {
60
- json: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
61
- }
62
- }
63
- ```
64
-
65
- If you want to restrict the output to only one of the three drivers (comment, media query or regular output) you can pass a flag as the second parameter with one of the four following keywords: `all`, `comment`, `media` or `regular`. Default is `all`.
66
-
67
- ### Decoding JSON to Sass
68
-
69
- ``` scss
70
- $json-decode: json-decode('{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}');
71
- // ("a": 1 2 ("b": 1), "b": #444444 false ("a": 1, "b": "test"), "c": 2 3 4 "string")
72
- ```
73
-
74
- ## Importing and decoding a JSON file
75
-
76
- To importe and decode an external `.json` file directly into a Sass variable:
77
-
78
- ``` scss
79
- @import 'relative/path/to/file.json?variable-name'
80
- // Do something with $variable-name
81
- ```
82
-
83
- **Important:**
84
-
85
- * the path to the JSON file is relative to importing file
86
- * the get parameter is the variable name to use it in Sass
87
-
88
- ## Requirements
89
-
90
- All you need is a clean version of Sass 3.3. Otherwise it's just pure Sass madness.
91
-
92
- ## Development
93
-
94
- ### You need
95
-
96
- * [NodeJS](http://nodejs.org)
97
- * [Ruby](https://www.ruby-lang.org/)
98
- * Sass 3.3 via `gem install sass --pre`
99
- * `grunt-cli` via `npm install -g grunt-cli`
100
-
101
- ### How to
102
-
103
- 1. Fork this repository
104
- 2. Run `npm install`
105
- 3. `grunt dev`
106
- 4. Make your changes + write tests
107
- 5. Commit + Pull request
108
-
109
- ## Credits
110
-
111
- * [Fabrice Weinberg](http://twitter.com/fweinb)
112
- * [Hugo Giraudel](http://twitter.com/hugogiraudel)
1
+ # SassyJSON [![NPM version](https://badge.fury.io/js/sassyjson.png)](http://badge.fury.io/js/sassyjson) [![Gem Version](https://badge.fury.io/rb/SassyJSON.png)](http://badge.fury.io/rb/SassyJSON) [![Build Status](https://travis-ci.org/HugoGiraudel/SassyJSON.png?branch=master)](https://travis-ci.org/HugoGiraudel/SassyJSON)
2
+
3
+ SassyJSON is a Sass-powered API for JSON. It provides you the classic `json-encode` and `json-decode` directly from your Sass files. We'll leave you the only judges of the point of this.
4
+
5
+ ## Install
6
+
7
+ SassyJSON is available on [npm](https://npmjs.org/) or as a [Ruby Gem](http://rubygems.org/gems/SassyJSON).
8
+
9
+ ### Git
10
+
11
+ ``` git
12
+ git clone https://github.com/HugoGiraudel/SassyJSON.git && cd SassyJSON
13
+ ```
14
+
15
+ ### npm
16
+
17
+ ``` bash
18
+ npm install sassyjson --save-dev
19
+ ```
20
+
21
+ ### Compass extension
22
+
23
+ 1. `gem install SassyJSON`
24
+ 2. Add `require 'SassyJSON'` to your `config.rb`
25
+ 3. Import it in your stylesheets with `@import 'SassyJSON'`
26
+
27
+ ### Sass
28
+
29
+ If you only want to play around the code without cloning the repo or using npm, you can find a [single file](https://github.com/HugoGiraudel/SassyJSON/blob/master/dist/_SassyJSON.scss) containing the whole API in the [dist](https://github.com/HugoGiraudel/SassyJSON/tree/master/dist) folder.
30
+
31
+ Also, SassyJSON is available at [Sassmeister](http://sassmeister.com/).
32
+
33
+ ## Example
34
+
35
+ ### Encoding Sass to JSON
36
+
37
+ #### Sass
38
+
39
+ ``` scss
40
+ $map: ((a: (1 2 ( b : 1 )), b: ( #444444, false, ( a: 1, b: test ) ), c: (2 3 4 string)));
41
+
42
+ @include json-encode($map);
43
+ ```
44
+
45
+ #### CSS
46
+
47
+ ``` css
48
+ /*! json-encode: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}' */
49
+
50
+ body::before {
51
+ content: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
52
+ }
53
+
54
+ head {
55
+ font-family: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
56
+ }
57
+
58
+ @media -json-encode {
59
+ json {
60
+ json: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
61
+ }
62
+ }
63
+ ```
64
+
65
+ If you want to restrict the output to only one of the three drivers (comment, media query or regular output) you can pass a flag as the second parameter with one of the four following keywords: `all`, `comment`, `media` or `regular`. Default is `all`.
66
+
67
+ ### Decoding JSON to Sass
68
+
69
+ ``` scss
70
+ $json-decode: json-decode('{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}');
71
+ // ("a": 1 2 ("b": 1), "b": #444444 false ("a": 1, "b": "test"), "c": 2 3 4 "string")
72
+ ```
73
+
74
+ ## Importing and decoding a JSON file
75
+
76
+ To importe and decode an external `.json` file directly into a Sass variable:
77
+
78
+ ``` scss
79
+ @import 'relative/path/to/file.json?variable-name'
80
+ // Do something with $variable-name
81
+ ```
82
+
83
+ **Important:**
84
+
85
+ * the path to the JSON file is relative to importing file
86
+ * the get parameter is the variable name to use it in Sass
87
+
88
+ ## Requirements
89
+
90
+ All you need is a clean version of Sass 3.3. Otherwise it's just pure Sass madness.
91
+
92
+ ## Development
93
+
94
+ ### You need
95
+
96
+ * [NodeJS](http://nodejs.org)
97
+ * [Ruby](https://www.ruby-lang.org/)
98
+ * Sass 3.3 via `gem install sass --pre`
99
+ * `grunt-cli` via `npm install -g grunt-cli`
100
+
101
+ ### How to
102
+
103
+ 1. Fork this repository
104
+ 2. Run `npm install`
105
+ 3. `grunt dev`
106
+ 4. Make your changes + write tests
107
+ 5. Commit + Pull request
108
+
109
+ ## Credits
110
+
111
+ * [Fabrice Weinberg](http://twitter.com/fweinb)
112
+ * [Hugo Giraudel](http://twitter.com/hugogiraudel)
data/lib/JsonImporter.rb CHANGED
@@ -1,229 +1,229 @@
1
- require 'sass'
2
-
3
-
4
- # Monkey Path Sass
5
- # Adapted from: https://github.com/chriseppstein/sass-css-importer
6
- class Sass::Engine
7
- alias initialize_without_json_importer initialize
8
-
9
- def initialize(template, options={})
10
- initialize_without_json_importer(template, options)
11
-
12
- json_importer = self.options[:load_paths].find {|lp| lp.is_a?(Sass::Importers::JsonImporter) }
13
-
14
- unless json_importer
15
- root = File.dirname(options[:filename] || ".")
16
- self.options[:load_paths] << Sass::Importers::JsonImporter.new(root)
17
- end
18
- end
19
- end
20
-
21
- module Sass
22
- module Importers
23
- # The default importer, used for any strings found in the load path.
24
- # Simply loads Sass files from the filesystem using the default logic.
25
- class JsonImporter < Base
26
-
27
- attr_accessor :root
28
-
29
- # Creates a new filesystem importer that imports files relative to a given path.
30
- #
31
- # @param root [String] The root path.
32
- # This importer will import files relative to this path.
33
- def initialize(root)
34
- @root = File.expand_path(root)
35
- @same_name_warnings = Set.new
36
- end
37
-
38
- # Enable watching of json files in Sass 3.3+
39
- def watched_directories
40
- [root]
41
- end
42
-
43
- # Enable watching of json files in Sass 3.3+
44
- def watched_file?(file)
45
- file.start_with?(root+File::SEPARATOR) && File.extname(file) == ".json"
46
- end
47
- # @see Base#find_relative
48
- def find_relative(name, base, options)
49
- _find(File.dirname(base), name, options)
50
- end
51
-
52
- # @see Base#find
53
- def find(name, options)
54
- _find(@root, name, options)
55
- end
56
-
57
- # @see Base#mtime
58
- def mtime(name, options)
59
- name = strip_varname(name);
60
- file, _ = Sass::Util.destructure(find_real_file(@root, name, options))
61
- File.mtime(file) if file
62
- rescue Errno::ENOENT
63
- nil
64
- end
65
-
66
- # @see Base#key
67
- def key(name, options)
68
- name = strip_varname(name);
69
- [self.class.name + ":" + File.dirname(File.expand_path(name)),
70
- File.basename(name)]
71
- end
72
-
73
- # @see Base#to_s
74
- def to_s
75
- @root
76
- end
77
-
78
- def hash
79
- @root.hash
80
- end
81
-
82
- def eql?(other)
83
- root.eql?(other.root)
84
- end
85
-
86
- protected
87
-
88
- # If a full uri is passed, this removes the root from it
89
- # otherwise returns the name unchanged
90
- def remove_root(name)
91
- if name.index(@root + "/") == 0
92
- name[(@root.length + 1)..-1]
93
- else
94
- name
95
- end
96
- end
97
-
98
- # A hash from file extensions to the syntaxes for those extensions.
99
- # The syntaxes must be `:sass` or `:scss`.
100
- #
101
- # This can be overridden by subclasses that want normal filesystem importing
102
- # with unusual extensions.
103
- #
104
- # @return [{String => Symbol}]
105
- def extensions
106
- {'json' => :scss}
107
- end
108
-
109
- # Given an `@import`ed path, returns an array of possible
110
- # on-disk filenames and their corresponding syntaxes for that path.
111
- #
112
- # @param name [String] The filename.
113
- # @return [Array(String, Symbol)] An array of pairs.
114
- # The first element of each pair is a filename to look for;
115
- # the second element is the syntax that file would be in (`:sass` or `:scss`).
116
- def possible_files(name)
117
- name = escape_glob_characters(name)
118
- dirname, basename, extname = split(name)
119
- sorted_exts = extensions.sort
120
- syntax = extensions[extname]
121
-
122
- if syntax
123
- ret = [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]]
124
- else
125
- ret = sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
126
- end
127
-
128
- # JRuby chokes when trying to import files from JARs when the path starts with './'.
129
- ret.map {|f, s| [f.sub(%r{^\./}, ''), s]}
130
- end
131
-
132
- def escape_glob_characters(name)
133
- name.gsub(/[\*\[\]\{\}\?]/) do |char|
134
- "\\#{char}"
135
- end
136
- end
137
-
138
- REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
139
- # Given a base directory and an `@import`ed name,
140
- # finds an existant file that matches the name.
141
- #
142
- # @param dir [String] The directory relative to which to search.
143
- # @param name [String] The filename to search for.
144
- # @return [(String, Symbol)] A filename-syntax pair.
145
- def find_real_file(dir, name, options)
146
- # on windows 'dir' can be in native File::ALT_SEPARATOR form
147
- dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
148
-
149
- found = possible_files(remove_root(name)).map do |f, s|
150
- path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{escape_glob_characters(dir)}/#{f}"
151
- Dir[path].map do |full_path|
152
- full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
153
- [Pathname.new(full_path).cleanpath.to_s, s]
154
- end
155
- end
156
- found = Sass::Util.flatten(found, 1)
157
- return if found.empty?
158
-
159
- if found.size > 1 && !@same_name_warnings.include?(found.first.first)
160
- found.each {|(f, _)| @same_name_warnings << f}
161
- relative_to = Pathname.new(dir)
162
- if options[:_line]
163
- # If _line exists, we're here due to an actual import in an
164
- # import_node and we want to print a warning for a user writing an
165
- # ambiguous import.
166
- candidates = found.map {|(f, _)| " " + Pathname.new(f).relative_path_from(relative_to).to_s}.join("\n")
167
- Sass::Util.sass_warn <<WARNING
168
- WARNING: On line #{options[:_line]}#{" of #{options[:filename]}" if options[:filename]}:
169
- It's not clear which file to import for '@import "#{name}"'.
170
- Candidates:
171
- #{candidates}
172
- For now I'll choose #{File.basename found.first.first}.
173
- This will be an error in future versions of Sass.
174
- WARNING
175
- else
176
- # Otherwise, we're here via StalenessChecker, and we want to print a
177
- # warning for a user running `sass --watch` with two ambiguous files.
178
- candidates = found.map {|(f, _)| " " + File.basename(f)}.join("\n")
179
- Sass::Util.sass_warn <<WARNING
180
- WARNING: In #{File.dirname(name)}:
181
- There are multiple files that match the name "#{File.basename(name)}":
182
- #{candidates}
183
- WARNING
184
- end
185
- end
186
- found.first
187
- end
188
-
189
- # Splits a filename into three parts, a directory part, a basename, and an extension
190
- # Only the known extensions returned from the extensions method will be recognized as such.
191
- def split(name)
192
- extension = nil
193
- dirname, basename = File.dirname(name), File.basename(name)
194
- if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
195
- basename = $1
196
- extension = $2
197
- end
198
- [dirname, basename, extension]
199
- end
200
-
201
- private
202
-
203
- def strip_varname(name)
204
- name.include?(".json?") ? name[0..name.rindex("?")-1] : name
205
- end
206
-
207
-
208
- def _find(dir, name, options)
209
- if name.include? ".json?"
210
- quotePos = name.rindex("?");
211
- path = name[0..quotePos-1]
212
- varname = name[quotePos+1..-1]
213
-
214
- full_filename, syntax = Sass::Util.destructure(find_real_file(dir, path, options))
215
- return unless full_filename && File.readable?(full_filename)
216
-
217
- options[:syntax] = syntax
218
- options[:filename] = full_filename
219
- options[:importer] = self
220
-
221
- Sass::Engine.new("$#{varname} : json_decode('" + File.read(full_filename) + "');", options)
222
- else
223
- Sass::Engine.new("$hello:'test';", options)
224
- end
225
- end
226
-
227
- end
228
- end
1
+ require 'sass'
2
+
3
+
4
+ # Monkey Path Sass
5
+ # Adapted from: https://github.com/chriseppstein/sass-css-importer
6
+ class Sass::Engine
7
+ alias initialize_without_json_importer initialize
8
+
9
+ def initialize(template, options={})
10
+ initialize_without_json_importer(template, options)
11
+
12
+ json_importer = self.options[:load_paths].find {|lp| lp.is_a?(Sass::Importers::JsonImporter) }
13
+
14
+ unless json_importer
15
+ root = File.dirname(options[:filename] || ".")
16
+ self.options[:load_paths] << Sass::Importers::JsonImporter.new(root)
17
+ end
18
+ end
19
+ end
20
+
21
+ module Sass
22
+ module Importers
23
+ # The default importer, used for any strings found in the load path.
24
+ # Simply loads Sass files from the filesystem using the default logic.
25
+ class JsonImporter < Base
26
+
27
+ attr_accessor :root
28
+
29
+ # Creates a new filesystem importer that imports files relative to a given path.
30
+ #
31
+ # @param root [String] The root path.
32
+ # This importer will import files relative to this path.
33
+ def initialize(root)
34
+ @root = File.expand_path(root)
35
+ @same_name_warnings = Set.new
36
+ end
37
+
38
+ # Enable watching of json files in Sass 3.3+
39
+ def watched_directories
40
+ [root]
41
+ end
42
+
43
+ # Enable watching of json files in Sass 3.3+
44
+ def watched_file?(file)
45
+ file.start_with?(root+File::SEPARATOR) && File.extname(file) == ".json"
46
+ end
47
+ # @see Base#find_relative
48
+ def find_relative(name, base, options)
49
+ _find(File.dirname(base), name, options)
50
+ end
51
+
52
+ # @see Base#find
53
+ def find(name, options)
54
+ _find(@root, name, options)
55
+ end
56
+
57
+ # @see Base#mtime
58
+ def mtime(name, options)
59
+ name = strip_varname(name);
60
+ file, _ = Sass::Util.destructure(find_real_file(@root, name, options))
61
+ File.mtime(file) if file
62
+ rescue Errno::ENOENT
63
+ nil
64
+ end
65
+
66
+ # @see Base#key
67
+ def key(name, options)
68
+ name = strip_varname(name);
69
+ [self.class.name + ":" + File.dirname(File.expand_path(name)),
70
+ File.basename(name)]
71
+ end
72
+
73
+ # @see Base#to_s
74
+ def to_s
75
+ @root
76
+ end
77
+
78
+ def hash
79
+ @root.hash
80
+ end
81
+
82
+ def eql?(other)
83
+ root.eql?(other.root)
84
+ end
85
+
86
+ protected
87
+
88
+ # If a full uri is passed, this removes the root from it
89
+ # otherwise returns the name unchanged
90
+ def remove_root(name)
91
+ if name.index(@root + "/") == 0
92
+ name[(@root.length + 1)..-1]
93
+ else
94
+ name
95
+ end
96
+ end
97
+
98
+ # A hash from file extensions to the syntaxes for those extensions.
99
+ # The syntaxes must be `:sass` or `:scss`.
100
+ #
101
+ # This can be overridden by subclasses that want normal filesystem importing
102
+ # with unusual extensions.
103
+ #
104
+ # @return [{String => Symbol}]
105
+ def extensions
106
+ {'json' => :scss}
107
+ end
108
+
109
+ # Given an `@import`ed path, returns an array of possible
110
+ # on-disk filenames and their corresponding syntaxes for that path.
111
+ #
112
+ # @param name [String] The filename.
113
+ # @return [Array(String, Symbol)] An array of pairs.
114
+ # The first element of each pair is a filename to look for;
115
+ # the second element is the syntax that file would be in (`:sass` or `:scss`).
116
+ def possible_files(name)
117
+ name = escape_glob_characters(name)
118
+ dirname, basename, extname = split(name)
119
+ sorted_exts = extensions.sort
120
+ syntax = extensions[extname]
121
+
122
+ if syntax
123
+ ret = [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]]
124
+ else
125
+ ret = sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
126
+ end
127
+
128
+ # JRuby chokes when trying to import files from JARs when the path starts with './'.
129
+ ret.map {|f, s| [f.sub(%r{^\./}, ''), s]}
130
+ end
131
+
132
+ def escape_glob_characters(name)
133
+ name.gsub(/[\*\[\]\{\}\?]/) do |char|
134
+ "\\#{char}"
135
+ end
136
+ end
137
+
138
+ REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
139
+ # Given a base directory and an `@import`ed name,
140
+ # finds an existant file that matches the name.
141
+ #
142
+ # @param dir [String] The directory relative to which to search.
143
+ # @param name [String] The filename to search for.
144
+ # @return [(String, Symbol)] A filename-syntax pair.
145
+ def find_real_file(dir, name, options)
146
+ # on windows 'dir' can be in native File::ALT_SEPARATOR form
147
+ dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
148
+
149
+ found = possible_files(remove_root(name)).map do |f, s|
150
+ path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{escape_glob_characters(dir)}/#{f}"
151
+ Dir[path].map do |full_path|
152
+ full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
153
+ [Pathname.new(full_path).cleanpath.to_s, s]
154
+ end
155
+ end
156
+ found = Sass::Util.flatten(found, 1)
157
+ return if found.empty?
158
+
159
+ if found.size > 1 && !@same_name_warnings.include?(found.first.first)
160
+ found.each {|(f, _)| @same_name_warnings << f}
161
+ relative_to = Pathname.new(dir)
162
+ if options[:_line]
163
+ # If _line exists, we're here due to an actual import in an
164
+ # import_node and we want to print a warning for a user writing an
165
+ # ambiguous import.
166
+ candidates = found.map {|(f, _)| " " + Pathname.new(f).relative_path_from(relative_to).to_s}.join("\n")
167
+ Sass::Util.sass_warn <<WARNING
168
+ WARNING: On line #{options[:_line]}#{" of #{options[:filename]}" if options[:filename]}:
169
+ It's not clear which file to import for '@import "#{name}"'.
170
+ Candidates:
171
+ #{candidates}
172
+ For now I'll choose #{File.basename found.first.first}.
173
+ This will be an error in future versions of Sass.
174
+ WARNING
175
+ else
176
+ # Otherwise, we're here via StalenessChecker, and we want to print a
177
+ # warning for a user running `sass --watch` with two ambiguous files.
178
+ candidates = found.map {|(f, _)| " " + File.basename(f)}.join("\n")
179
+ Sass::Util.sass_warn <<WARNING
180
+ WARNING: In #{File.dirname(name)}:
181
+ There are multiple files that match the name "#{File.basename(name)}":
182
+ #{candidates}
183
+ WARNING
184
+ end
185
+ end
186
+ found.first
187
+ end
188
+
189
+ # Splits a filename into three parts, a directory part, a basename, and an extension
190
+ # Only the known extensions returned from the extensions method will be recognized as such.
191
+ def split(name)
192
+ extension = nil
193
+ dirname, basename = File.dirname(name), File.basename(name)
194
+ if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
195
+ basename = $1
196
+ extension = $2
197
+ end
198
+ [dirname, basename, extension]
199
+ end
200
+
201
+ private
202
+
203
+ def strip_varname(name)
204
+ name.include?(".json?") ? name[0..name.rindex("?")-1] : name
205
+ end
206
+
207
+
208
+ def _find(dir, name, options)
209
+ if name.include? ".json?"
210
+ quotePos = name.rindex("?");
211
+ path = name[0..quotePos-1]
212
+ varname = name[quotePos+1..-1]
213
+
214
+ full_filename, syntax = Sass::Util.destructure(find_real_file(dir, path, options))
215
+ return unless full_filename && File.readable?(full_filename)
216
+
217
+ options[:syntax] = syntax
218
+ options[:filename] = full_filename
219
+ options[:importer] = self
220
+
221
+ Sass::Engine.new("$#{varname} : json_decode('" + File.read(full_filename) + "');", options)
222
+ else
223
+ Sass::Engine.new("$hello:'test';", options)
224
+ end
225
+ end
226
+
227
+ end
228
+ end
229
229
  end