haml 2.2.24 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (168) hide show
  1. data/.yardopts +0 -1
  2. data/README.md +91 -151
  3. data/REMEMBER +11 -1
  4. data/Rakefile +73 -55
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/css2sass +7 -1
  8. data/bin/sass-convert +7 -0
  9. data/extra/haml-mode.el +2 -1
  10. data/lib/haml/buffer.rb +22 -4
  11. data/lib/haml/engine.rb +5 -1
  12. data/lib/haml/exec.rb +231 -46
  13. data/lib/haml/filters.rb +19 -8
  14. data/lib/haml/helpers.rb +47 -20
  15. data/lib/haml/helpers/action_view_extensions.rb +2 -4
  16. data/lib/haml/helpers/action_view_mods.rb +11 -8
  17. data/lib/haml/helpers/xss_mods.rb +13 -2
  18. data/lib/haml/html.rb +179 -48
  19. data/lib/haml/html/erb.rb +141 -0
  20. data/lib/haml/precompiler.rb +40 -15
  21. data/lib/haml/railtie.rb +1 -5
  22. data/lib/haml/root.rb +3 -0
  23. data/lib/haml/template.rb +4 -14
  24. data/lib/haml/util.rb +120 -30
  25. data/lib/haml/version.rb +25 -2
  26. data/lib/sass.rb +5 -1
  27. data/lib/sass/callbacks.rb +50 -0
  28. data/lib/sass/css.rb +40 -191
  29. data/lib/sass/engine.rb +170 -74
  30. data/lib/sass/environment.rb +8 -2
  31. data/lib/sass/error.rb +163 -25
  32. data/lib/sass/files.rb +31 -28
  33. data/lib/sass/plugin.rb +268 -87
  34. data/lib/sass/plugin/rails.rb +9 -4
  35. data/lib/sass/repl.rb +1 -1
  36. data/lib/sass/script.rb +31 -29
  37. data/lib/sass/script/bool.rb +1 -0
  38. data/lib/sass/script/color.rb +290 -23
  39. data/lib/sass/script/css_lexer.rb +22 -0
  40. data/lib/sass/script/css_parser.rb +28 -0
  41. data/lib/sass/script/funcall.rb +22 -3
  42. data/lib/sass/script/functions.rb +523 -33
  43. data/lib/sass/script/interpolation.rb +42 -0
  44. data/lib/sass/script/lexer.rb +169 -52
  45. data/lib/sass/script/literal.rb +58 -9
  46. data/lib/sass/script/node.rb +79 -1
  47. data/lib/sass/script/number.rb +20 -5
  48. data/lib/sass/script/operation.rb +49 -3
  49. data/lib/sass/script/parser.rb +162 -28
  50. data/lib/sass/script/string.rb +50 -2
  51. data/lib/sass/script/unary_operation.rb +25 -2
  52. data/lib/sass/script/variable.rb +21 -4
  53. data/lib/sass/scss.rb +14 -0
  54. data/lib/sass/scss/css_parser.rb +39 -0
  55. data/lib/sass/scss/parser.rb +683 -0
  56. data/lib/sass/scss/rx.rb +112 -0
  57. data/lib/sass/scss/script_lexer.rb +13 -0
  58. data/lib/sass/scss/script_parser.rb +25 -0
  59. data/lib/sass/tree/comment_node.rb +69 -27
  60. data/lib/sass/tree/debug_node.rb +7 -2
  61. data/lib/sass/tree/directive_node.rb +41 -35
  62. data/lib/sass/tree/for_node.rb +6 -0
  63. data/lib/sass/tree/if_node.rb +13 -1
  64. data/lib/sass/tree/import_node.rb +52 -27
  65. data/lib/sass/tree/mixin_def_node.rb +18 -0
  66. data/lib/sass/tree/mixin_node.rb +41 -6
  67. data/lib/sass/tree/node.rb +197 -70
  68. data/lib/sass/tree/prop_node.rb +152 -57
  69. data/lib/sass/tree/root_node.rb +118 -0
  70. data/lib/sass/tree/rule_node.rb +193 -96
  71. data/lib/sass/tree/variable_node.rb +9 -5
  72. data/lib/sass/tree/while_node.rb +4 -0
  73. data/test/benchmark.rb +5 -5
  74. data/test/haml/engine_test.rb +147 -10
  75. data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
  76. data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
  77. data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
  78. data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
  79. data/test/haml/helper_test.rb +91 -24
  80. data/test/haml/html2haml/erb_tests.rb +410 -0
  81. data/test/haml/html2haml_test.rb +210 -66
  82. data/test/haml/results/filters.xhtml +1 -1
  83. data/test/haml/results/just_stuff.xhtml +2 -0
  84. data/test/haml/spec_test.rb +44 -0
  85. data/test/haml/template_test.rb +22 -2
  86. data/test/haml/templates/helpers.haml +0 -13
  87. data/test/haml/templates/just_stuff.haml +2 -0
  88. data/test/haml/util_test.rb +48 -0
  89. data/test/sass/callbacks_test.rb +61 -0
  90. data/test/sass/conversion_test.rb +884 -0
  91. data/test/sass/css2sass_test.rb +99 -18
  92. data/test/sass/data/hsl-rgb.txt +319 -0
  93. data/test/sass/engine_test.rb +1049 -131
  94. data/test/sass/functions_test.rb +398 -47
  95. data/test/sass/more_results/more_import.css +1 -1
  96. data/test/sass/more_templates/more_import.sass +3 -3
  97. data/test/sass/plugin_test.rb +184 -10
  98. data/test/sass/results/compact.css +1 -1
  99. data/test/sass/results/complex.css +5 -5
  100. data/test/sass/results/compressed.css +1 -1
  101. data/test/sass/results/expanded.css +1 -1
  102. data/test/sass/results/import.css +3 -1
  103. data/test/sass/results/mixins.css +12 -12
  104. data/test/sass/results/nested.css +1 -1
  105. data/test/sass/results/options.css +1 -0
  106. data/test/sass/results/parent_ref.css +4 -4
  107. data/test/sass/results/script.css +3 -3
  108. data/test/sass/results/scss_import.css +15 -0
  109. data/test/sass/results/scss_importee.css +2 -0
  110. data/test/sass/script_conversion_test.rb +153 -0
  111. data/test/sass/script_test.rb +137 -70
  112. data/test/sass/scss/css_test.rb +811 -0
  113. data/test/sass/scss/rx_test.rb +156 -0
  114. data/test/sass/scss/scss_test.rb +871 -0
  115. data/test/sass/scss/test_helper.rb +37 -0
  116. data/test/sass/templates/alt.sass +2 -2
  117. data/test/sass/templates/bork1.sass +2 -0
  118. data/test/sass/templates/bork3.sass +2 -0
  119. data/test/sass/templates/bork4.sass +2 -0
  120. data/test/sass/templates/import.sass +4 -4
  121. data/test/sass/templates/importee.sass +3 -3
  122. data/test/sass/templates/line_numbers.sass +1 -1
  123. data/test/sass/templates/mixin_bork.sass +5 -0
  124. data/test/sass/templates/mixins.sass +2 -2
  125. data/test/sass/templates/nested_bork1.sass +2 -0
  126. data/test/sass/templates/nested_bork2.sass +2 -0
  127. data/test/sass/templates/nested_bork3.sass +2 -0
  128. data/test/sass/templates/nested_bork4.sass +2 -0
  129. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  130. data/test/sass/templates/options.sass +2 -0
  131. data/test/sass/templates/parent_ref.sass +2 -2
  132. data/test/sass/templates/script.sass +69 -69
  133. data/test/sass/templates/scss_import.scss +10 -0
  134. data/test/sass/templates/scss_importee.scss +1 -0
  135. data/test/sass/templates/units.sass +10 -10
  136. data/test/test_helper.rb +20 -8
  137. data/vendor/fssm/LICENSE +20 -0
  138. data/vendor/fssm/README.markdown +55 -0
  139. data/vendor/fssm/Rakefile +59 -0
  140. data/vendor/fssm/VERSION.yml +5 -0
  141. data/vendor/fssm/example.rb +9 -0
  142. data/vendor/fssm/fssm.gemspec +77 -0
  143. data/vendor/fssm/lib/fssm.rb +33 -0
  144. data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  145. data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  146. data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  147. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  148. data/vendor/fssm/lib/fssm/monitor.rb +26 -0
  149. data/vendor/fssm/lib/fssm/path.rb +91 -0
  150. data/vendor/fssm/lib/fssm/pathname.rb +502 -0
  151. data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  152. data/vendor/fssm/lib/fssm/state/file.rb +24 -0
  153. data/vendor/fssm/lib/fssm/support.rb +63 -0
  154. data/vendor/fssm/lib/fssm/tree.rb +176 -0
  155. data/vendor/fssm/profile/prof-cache.rb +40 -0
  156. data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  157. data/vendor/fssm/profile/prof-pathname.rb +68 -0
  158. data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  159. data/vendor/fssm/profile/prof.html +2379 -0
  160. data/vendor/fssm/spec/path_spec.rb +75 -0
  161. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  162. data/vendor/fssm/spec/root/file.css +0 -0
  163. data/vendor/fssm/spec/root/file.rb +0 -0
  164. data/vendor/fssm/spec/root/file.yml +0 -0
  165. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  166. data/vendor/fssm/spec/spec_helper.rb +14 -0
  167. metadata +94 -14
  168. data/test/sass/templates/bork.sass +0 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.24
1
+ 3.0.0.beta.1
@@ -1 +1 @@
1
- Powerful Penny
1
+ Classy Cassidy
@@ -3,5 +3,11 @@
3
3
  require File.dirname(__FILE__) + '/../lib/haml'
4
4
  require 'haml/exec'
5
5
 
6
- opts = Haml::Exec::CSS2Sass.new(ARGV)
6
+ warn <<END
7
+ DEPRECATION WARNING:
8
+ The css2sass tool is deprecated and will be removed in Sass 3.2.
9
+ Use the sass-convert tool instead.
10
+ END
11
+
12
+ opts = Haml::Exec::SassConvert.new(%w[--from css --to sass] + ARGV)
7
13
  opts.parse!
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/haml'
4
+ require 'haml/exec'
5
+
6
+ opts = Haml::Exec::SassConvert.new(ARGV)
7
+ opts.parse!
@@ -352,7 +352,8 @@ With ARG, do it that many times."
352
352
  ;; Move through multiline attrs
353
353
  (when (eq (char-before) ?,)
354
354
  (save-excursion
355
- (while (progn (end-of-line) (and (eq (char-before) ?,) (not (eobp))))
355
+ (while (progn (end-of-line)
356
+ (and (eq (char-before) ?,) (not (eobp))))
356
357
  (forward-line))
357
358
 
358
359
  (forward-line -1)
@@ -233,30 +233,43 @@ RUBY
233
233
 
234
234
  # Merges two attribute hashes.
235
235
  # This is the same as `to.merge!(from)`,
236
- # except that it merges id and class attributes.
236
+ # except that it merges id, class, and data attributes.
237
237
  #
238
238
  # ids are concatenated with `"_"`,
239
239
  # and classes are concatenated with `" "`.
240
+ # data hashes are simply merged.
240
241
  #
241
242
  # Destructively modifies both `to` and `from`.
242
243
  #
243
244
  # @param to [{String => String}] The attribute hash to merge into
244
- # @param from [{String => String}] The attribute hash to merge from
245
+ # @param from [{String => #to_s}] The attribute hash to merge from
245
246
  # @return [{String => String}] `to`, after being merged
246
247
  def self.merge_attrs(to, from)
248
+ from['id'] = Precompiler.filter_and_join(from['id'], '_') if from['id']
247
249
  if to['id'] && from['id']
248
250
  to['id'] << '_' << from.delete('id').to_s
249
251
  elsif to['id'] || from['id']
250
252
  from['id'] ||= to['id']
251
253
  end
252
254
 
255
+ from['class'] = Precompiler.filter_and_join(from['class'], ' ') if from['class']
253
256
  if to['class'] && from['class']
254
257
  # Make sure we don't duplicate class names
255
- from['class'] = (from['class'].split(' ') | to['class'].split(' ')).sort.join(' ')
258
+ from['class'] = (from['class'].to_s.split(' ') | to['class'].split(' ')).sort.join(' ')
256
259
  elsif to['class'] || from['class']
257
260
  from['class'] ||= to['class']
258
261
  end
259
262
 
263
+ from_data = from['data'].is_a?(Hash)
264
+ to_data = to['data'].is_a?(Hash)
265
+ if from_data && to_data
266
+ to['data'] = to['data'].merge(from['data'])
267
+ elsif to_data
268
+ to = Haml::Util.map_keys(to.delete('data')) {|name| "data-#{name}"}.merge(to)
269
+ elsif from_data
270
+ from = Haml::Util.map_keys(from.delete('data')) {|name| "data-#{name}"}.merge(from)
271
+ end
272
+
260
273
  to.merge!(from)
261
274
  end
262
275
 
@@ -278,7 +291,12 @@ RUBY
278
291
  ref = ref[0]
279
292
  # Let's make sure the value isn't nil. If it is, return the default Hash.
280
293
  return {} if ref.nil?
281
- class_name = underscore(ref.class)
294
+ class_name =
295
+ if ref.respond_to?(:haml_object_ref)
296
+ ref.haml_object_ref
297
+ else
298
+ underscore(ref.class)
299
+ end
282
300
  id = "#{class_name}_#{ref.id || 'new'}"
283
301
  if prefix
284
302
  class_name = "#{ prefix }_#{ class_name}"
@@ -99,6 +99,8 @@ module Haml
99
99
  @options[:encoding] = @options[:encoding].name
100
100
  end
101
101
 
102
+ template = check_encoding(template) {|msg, line| raise Haml::Error.new(msg, line)}
103
+
102
104
  # :eod is a special end-of-document marker
103
105
  @template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
104
106
  @template_index = 0
@@ -113,7 +115,9 @@ module Haml
113
115
 
114
116
  precompile
115
117
  rescue Haml::Error => e
116
- e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}" if @index
118
+ if @index || e.line
119
+ e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}"
120
+ end
117
121
  raise
118
122
  end
119
123
 
@@ -3,7 +3,7 @@ require 'fileutils'
3
3
  require 'rbconfig'
4
4
 
5
5
  module Haml
6
- # This module handles the various Haml executables (`haml`, `sass`, `css2sass`, etc).
6
+ # This module handles the various Haml executables (`haml`, `sass`, `sass-convert`, etc).
7
7
  module Exec
8
8
  # An abstract class that encapsulates the executable code for all three executables.
9
9
  class Generic
@@ -93,17 +93,14 @@ module Haml
93
93
  # so they can run their respective programs.
94
94
  def process_result
95
95
  input, output = @options[:input], @options[:output]
96
- input_file, output_file = if input
97
- [nil, open_file(@args[0], 'w')]
98
- else
99
- @options[:filename] = @args[0]
100
- [open_file(@args[0]), open_file(@args[1], 'w')]
101
- end
102
-
103
- input ||= input_file
104
- output ||= output_file
105
- input ||= $stdin
106
- output ||= $stdout
96
+ args = @args.dup
97
+ input ||=
98
+ begin
99
+ filename = args.shift
100
+ @options[:filename] = filename
101
+ open_file(filename) || $stdin
102
+ end
103
+ output ||= open_file(args.shift, 'w') || $stdout
107
104
 
108
105
  @options[:input], @options[:output] = input, output
109
106
  end
@@ -218,10 +215,28 @@ END
218
215
  def set_opts(opts)
219
216
  super
220
217
 
218
+ opts.on('--scss',
219
+ 'Use the CSS-superset SCSS syntax.') do
220
+ @options[:for_engine][:syntax] = :scss
221
+ end
222
+ opts.on('--watch', 'Watch files or directories for changes.',
223
+ 'The location of the generated CSS can be set using a colon:',
224
+ ' sass --watch input.sass:output.css',
225
+ ' sass --watch input-dir:output-dir') do
226
+ @options[:watch] = true
227
+ end
228
+ opts.on('--update', 'Compile files or directories to CSS.',
229
+ 'Locations are set like --watch.') do
230
+ @options[:update] = true
231
+ end
221
232
  opts.on('-t', '--style NAME',
222
233
  'Output style. Can be nested (default), compact, compressed, or expanded.') do |name|
223
234
  @options[:for_engine][:style] = name.to_sym
224
235
  end
236
+ opts.on('-g', '--debug-info',
237
+ 'Emit extra information in the generated CSS that can be used by the FireSass Firebug plugin.') do
238
+ @options[:for_engine][:debug_info] = true
239
+ end
225
240
  opts.on('-l', '--line-numbers', '--line-comments',
226
241
  'Emit comments in the generated CSS indicating the corresponding sass line.') do
227
242
  @options[:for_engine][:line_numbers] = true
@@ -244,19 +259,24 @@ END
244
259
  # Processes the options set by the command-line arguments,
245
260
  # and runs the Sass compiler appropriately.
246
261
  def process_result
247
- if @options[:interactive]
248
- require 'sass'
249
- require 'sass/repl'
250
- ::Sass::Repl.new(@options).run
251
- return
262
+ if !@options[:update] && !@options[:watch] &&
263
+ @args.first && @args.first.include?(':')
264
+ if @args.size == 1
265
+ @args = @args.first.split(':', 2)
266
+ else
267
+ @options[:update] = true
268
+ end
252
269
  end
253
270
 
271
+ return interactive if @options[:interactive]
272
+ return watch_or_update if @options[:watch] || @options[:update]
254
273
  super
255
274
 
256
275
  begin
257
276
  input = @options[:input]
258
277
  output = @options[:output]
259
278
 
279
+ @options[:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
260
280
  tree =
261
281
  if input.is_a?(File) && !@options[:check_syntax]
262
282
  ::Sass::Files.tree_for(input.path, @options[:for_engine])
@@ -273,8 +293,87 @@ END
273
293
  output.close() if output.is_a? File
274
294
  rescue ::Sass::SyntaxError => e
275
295
  raise e if @options[:trace]
276
- raise "Syntax error on line #{get_line e}: #{e.message}"
296
+ raise e.sass_backtrace_str("standard input")
297
+ end
298
+ end
299
+
300
+ private
301
+
302
+ def interactive
303
+ require 'sass'
304
+ require 'sass/repl'
305
+ ::Sass::Repl.new(@options).run
306
+ end
307
+
308
+ def watch_or_update
309
+ require 'sass'
310
+ require 'sass/plugin'
311
+ ::Sass::Plugin.options.merge! @options[:for_engine]
312
+ ::Sass::Plugin.options[:unix_newlines] = @options[:unix_newlines]
313
+
314
+ if @args[1] && !@args[0].include?(':')
315
+ flag = @options[:update] ? "--update" : "--watch"
316
+ err =
317
+ if !File.exist?(@args[1])
318
+ "doesn't exist"
319
+ elsif @args[1] =~ /\.css$/
320
+ "is a CSS file"
321
+ end
322
+ raise <<MSG if err
323
+ File #{@args[1]} #{err}.
324
+ Did you mean: sass #{flag} #{@args[0]}:#{@args[1]}
325
+ MSG
326
+ end
327
+
328
+ dirs, files = @args.map {|name| name.split(':', 2)}.
329
+ map {|from, to| [from, to || from.gsub(/\..*?$/, '.css')]}.
330
+ partition {|i, _| File.directory? i}
331
+ ::Sass::Plugin.options[:template_location] = dirs
332
+
333
+ ::Sass::Plugin.on_updating_stylesheet do |_, css|
334
+ if File.exists? css
335
+ puts_action :overwrite, :yellow, css
336
+ else
337
+ puts_action :create, :green, css
338
+ end
277
339
  end
340
+
341
+ ::Sass::Plugin.on_creating_directory {|dirname| puts_action :directory, :green, dirname}
342
+ ::Sass::Plugin.on_deleting_css {|filename| puts_action :delete, :yellow, filename}
343
+ ::Sass::Plugin.on_compilation_error do |error, _, _|
344
+ raise error unless error.is_a?(::Sass::SyntaxError)
345
+ puts_action :error, :red, "#{error.sass_filename} (Line #{error.sass_line}: #{error.message})"
346
+ end
347
+
348
+ if @options[:update]
349
+ ::Sass::Plugin.update_stylesheets(files)
350
+ return
351
+ end
352
+
353
+ puts ">>> Sass is watching for changes. Press Ctrl-C to stop."
354
+
355
+ ::Sass::Plugin.on_template_modified {|template| puts ">>> Change detected to: #{template}"}
356
+ ::Sass::Plugin.on_template_created {|template| puts ">>> New template detected: #{template}"}
357
+ ::Sass::Plugin.on_template_deleted {|template| puts ">>> Deleted template detected: #{template}"}
358
+
359
+ ::Sass::Plugin.watch(files)
360
+ end
361
+
362
+ # @private
363
+ COLORS = { :red => 31, :green => 32, :yellow => 33 }
364
+
365
+ def puts_action(name, color, arg)
366
+ printf color(color, "%11s %s\n"), name, arg
367
+ end
368
+
369
+ def color(color, str)
370
+ raise "[BUG] Unrecognized color #{color}" unless COLORS[color]
371
+
372
+ # Almost any real Unix terminal will support color,
373
+ # so we just filter for Windows terms (which don't set TERM)
374
+ # and not-real terminals, which aren't ttys.
375
+ return str if ENV["TERM"].empty? || !STDOUT.tty?
376
+ return "\e[#{COLORS[color]}m#{str}\e[0m"
278
377
  end
279
378
  end
280
379
 
@@ -309,6 +408,11 @@ END
309
408
  @options[:for_engine][:escape_html] = true
310
409
  end
311
410
 
411
+ opts.on('-q', '--double-quote-attributes',
412
+ 'Set attribute wrapper to double-quotes (default is single).') do
413
+ @options[:for_engine][:attr_wrapper] = '"'
414
+ end
415
+
312
416
  opts.on('-r', '--require FILE', "Same as 'ruby -r'.") do |file|
313
417
  @options[:requires] << file
314
418
  end
@@ -368,17 +472,7 @@ END
368
472
  # @param args [Array<String>] The command-line arguments
369
473
  def initialize(args)
370
474
  super
371
-
372
475
  @module_opts = {}
373
-
374
- begin
375
- require 'haml/html'
376
- rescue LoadError => err
377
- dep = err.message.scan(/^no such file to load -- (.*)/)[0]
378
- raise err if @options[:trace] || dep.nil? || dep.empty?
379
- $stderr.puts "Required dependency #{dep} not found!\n Use --trace for backtrace."
380
- exit 1
381
- end
382
476
  end
383
477
 
384
478
  # Tells optparse how to parse the arguments.
@@ -393,12 +487,20 @@ Description: Transforms an HTML file into corresponding Haml code.
393
487
  Options:
394
488
  END
395
489
 
396
- opts.on('-r', '--rhtml', 'Parse RHTML tags.') do
397
- @module_opts[:rhtml] = true
490
+ opts.on('-e', '--erb', 'Parse ERb tags.') do
491
+ @module_opts[:erb] = true
492
+ end
493
+
494
+ opts.on('--no-erb', "Don't parse ERb tags.") do
495
+ @options[:no_erb] = true
496
+ end
497
+
498
+ opts.on('-r', '--rhtml', 'Deprecated; same as --erb.') do
499
+ @module_opts[:erb] = true
398
500
  end
399
501
 
400
- opts.on('--no-rhtml', "Don't parse RHTML tags.") do
401
- @options[:no_rhtml] = true
502
+ opts.on('--no-rhtml', "Deprecated; same as --no-erb.") do
503
+ @options[:no_erb] = true
402
504
  end
403
505
 
404
506
  opts.on('-x', '--xhtml', 'Parse the input using the more strict XHTML parser.') do
@@ -413,25 +515,33 @@ END
413
515
  def process_result
414
516
  super
415
517
 
518
+ require 'haml/html'
519
+
416
520
  input = @options[:input]
417
521
  output = @options[:output]
418
522
 
419
- @module_opts[:rhtml] ||= input.respond_to?(:path) && input.path =~ /\.(rhtml|erb)$/
420
- @module_opts[:rhtml] &&= @options[:no_rhtml] != false
523
+ @module_opts[:erb] ||= input.respond_to?(:path) && input.path =~ /\.(rhtml|erb)$/
524
+ @module_opts[:erb] &&= @options[:no_erb] != false
421
525
 
422
526
  output.write(::Haml::HTML.new(input, @module_opts).render)
527
+ rescue ::Haml::Error => e
528
+ raise "#{e.is_a?(::Haml::SyntaxError) ? "Syntax error" : "Error"} on line " +
529
+ "#{get_line e}: #{e.message}"
530
+ rescue LoadError => err
531
+ dep = err.message.scan(/^no such file to load -- (.*)/)[0]
532
+ raise err if @options[:trace] || dep.nil? || dep.empty?
533
+ $stderr.puts "Required dependency #{dep} not found!\n Use --trace for backtrace."
534
+ exit 1
423
535
  end
424
536
  end
425
537
 
426
- # The `css2sass` executable.
427
- class CSS2Sass < Generic
538
+ # The `sass-convert` executable.
539
+ class SassConvert < Generic
428
540
  # @param args [Array<String>] The command-line arguments
429
541
  def initialize(args)
430
542
  super
431
-
432
- @module_opts = {}
433
-
434
- require 'sass/css'
543
+ @options[:for_tree] = {}
544
+ @options[:for_engine] = {}
435
545
  end
436
546
 
437
547
  # Tells optparse how to parse the arguments.
@@ -439,18 +549,45 @@ END
439
549
  # @param opts [OptionParser]
440
550
  def set_opts(opts)
441
551
  opts.banner = <<END
442
- Usage: css2sass [options] [INPUT] [OUTPUT]
552
+ Usage: sass-convert [options] [INPUT] [OUTPUT]
443
553
 
444
- Description: Transforms a CSS file into corresponding Sass code.
554
+ Description:
555
+ Converts between CSS, Sass, and SCSS files.
556
+ E.g. converts from SCSS to Sass,
557
+ or converts from CSS to SCSS (adding appropriate nesting).
445
558
 
446
559
  Options:
447
560
  END
448
561
 
449
- opts.on('--old', 'Output the old-style ":prop val" property syntax') do
450
- @module_opts[:old] = true
562
+ opts.on('-F', '--from FORMAT',
563
+ 'The format to convert from. Can be css, scss, sass, or sass2.',
564
+ 'sass2 is the same as sass, but updates more old syntax to new.',
565
+ 'By default, this is inferred from the input filename.',
566
+ 'If there is none, defaults to css.') do |name|
567
+ @options[:from] = name.downcase.to_sym
568
+ end
569
+
570
+ opts.on('-T', '--to FORMAT',
571
+ 'The format to convert to. Can be scss or sass.',
572
+ 'By default, this is inferred from the output filename.',
573
+ 'If there is none, defaults to sass.') do |name|
574
+ @options[:to] = name.downcase.to_sym
575
+ end
576
+
577
+ opts.on('-i', '--in-place',
578
+ 'Convert a file to its own syntax.',
579
+ 'This can be used to update some deprecated syntax.') do
580
+ @options[:in_place] = true
451
581
  end
452
582
 
453
- opts.on_tail('-a', '--alternate', 'Ignored') {}
583
+ opts.on('--old', 'Output the old-style ":prop val" property syntax.',
584
+ 'Only meaningful when generating Sass.') do
585
+ @options[:for_tree][:old] = true
586
+ end
587
+
588
+ opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
589
+ @options[:for_engine][:cache] = false
590
+ end
454
591
 
455
592
  super
456
593
  end
@@ -458,12 +595,60 @@ END
458
595
  # Processes the options set by the command-line arguments,
459
596
  # and runs the CSS compiler appropriately.
460
597
  def process_result
598
+ require 'sass'
461
599
  super
462
600
 
463
601
  input = @options[:input]
464
602
  output = @options[:output]
603
+ output = input if @options[:in_place]
604
+
605
+ if input.is_a?(File)
606
+ @options[:from] ||=
607
+ case input.path
608
+ when /\.scss$/; :scss
609
+ when /\.sass$/; :sass
610
+ when /\.css$/; :css
611
+ end
612
+ elsif @options[:in_place]
613
+ raise "Error: the --in-place option requires a filename."
614
+ end
615
+
616
+ if output.is_a?(File)
617
+ @options[:to] ||=
618
+ case output.path
619
+ when /\.scss$/; :scss
620
+ when /\.sass$/; :sass
621
+ end
622
+ end
623
+
624
+ if @options[:from] == :sass2
625
+ @options[:from] = :sass
626
+ @options[:for_engine][:sass2] = true
627
+ end
628
+
629
+ @options[:from] ||= :css
630
+ @options[:to] ||= :sass
631
+ @options[:for_engine][:syntax] = @options[:from]
632
+
633
+ out =
634
+ ::Haml::Util.silence_haml_warnings do
635
+ if @options[:from] == :css
636
+ require 'sass/css'
637
+ ::Sass::CSS.new(input.read, @options[:for_tree]).render(@options[:to])
638
+ else
639
+ if input.is_a?(File)
640
+ ::Sass::Files.tree_for(input.path, @options[:for_engine])
641
+ else
642
+ ::Sass::Engine.new(input.read, @options[:for_engine]).to_tree
643
+ end.send("to_#{@options[:to]}", @options[:for_tree])
644
+ end
645
+ end
465
646
 
466
- output.write(::Sass::CSS.new(input, @module_opts).render)
647
+ output = File.open(input.path, 'w') if @options[:in_place]
648
+ output.write(out)
649
+ rescue ::Sass::SyntaxError => e
650
+ raise e if @options[:trace]
651
+ raise "Syntax error on line #{get_line e}: #{e.message}\n Use --trace for backtrace"
467
652
  end
468
653
  end
469
654
  end