2007-02-01 06:20:05 +00:00
|
|
|
require 'optparse'
|
2008-05-24 12:13:36 -07:00
|
|
|
require 'fileutils'
|
2010-01-23 12:52:30 -08:00
|
|
|
require 'rbconfig'
|
2007-02-01 06:20:05 +00:00
|
|
|
|
|
|
|
module Haml
|
2009-04-29 15:53:47 -07:00
|
|
|
# This module handles the various Haml executables (`haml`, `sass`, `css2sass`, etc).
|
|
|
|
module Exec
|
|
|
|
# An abstract class that encapsulates the executable code for all three executables.
|
|
|
|
class Generic
|
|
|
|
# @param args [Array<String>] The command-line arguments
|
2007-02-01 06:20:05 +00:00
|
|
|
def initialize(args)
|
|
|
|
@args = args
|
|
|
|
@options = {}
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Parses the command-line arguments and runs the executable.
|
|
|
|
# Calls `Kernel#exit` at the end, so it never returns.
|
2007-02-01 06:20:05 +00:00
|
|
|
def parse!
|
2007-04-01 07:21:41 +00:00
|
|
|
begin
|
2007-12-08 05:08:51 +00:00
|
|
|
@opts = OptionParser.new(&method(:set_opts))
|
2007-04-01 07:21:41 +00:00
|
|
|
@opts.parse!(@args)
|
|
|
|
|
|
|
|
process_result
|
2008-04-07 23:09:17 -07:00
|
|
|
|
2007-04-01 07:21:41 +00:00
|
|
|
@options
|
|
|
|
rescue Exception => e
|
2008-10-16 18:21:35 -07:00
|
|
|
raise e if @options[:trace] || e.is_a?(SystemExit)
|
2007-04-01 10:04:29 +00:00
|
|
|
|
2007-12-08 04:06:36 +00:00
|
|
|
$stderr.puts e.message
|
2007-04-01 07:21:41 +00:00
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
exit 0
|
2007-02-01 06:20:05 +00:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# @return [String] A description of the executable
|
2007-02-01 06:20:05 +00:00
|
|
|
def to_s
|
|
|
|
@opts.to_s
|
|
|
|
end
|
2007-12-08 04:06:36 +00:00
|
|
|
|
|
|
|
protected
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Finds the line of the source template
|
|
|
|
# on which an exception was raised.
|
|
|
|
#
|
|
|
|
# @param exception [Exception] The exception
|
|
|
|
# @return [String] The line number
|
2007-12-08 04:06:36 +00:00
|
|
|
def get_line(exception)
|
2008-04-18 10:06:04 -07:00
|
|
|
# SyntaxErrors have weird line reporting
|
|
|
|
# when there's trailing whitespace,
|
|
|
|
# which there is for Haml documents.
|
2009-02-07 02:33:58 -08:00
|
|
|
return exception.message.scan(/:(\d+)/).first.first if exception.is_a?(::SyntaxError)
|
|
|
|
exception.backtrace[0].scan(/:(\d+)/).first.first
|
2007-12-08 04:06:36 +00:00
|
|
|
end
|
2008-04-07 23:09:17 -07:00
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Tells optparse how to parse the arguments
|
|
|
|
# available for all executables.
|
|
|
|
#
|
|
|
|
# This is meant to be overridden by subclasses
|
|
|
|
# so they can add their own options.
|
|
|
|
#
|
|
|
|
# @param opts [OptionParser]
|
2007-02-01 06:20:05 +00:00
|
|
|
def set_opts(opts)
|
2007-12-08 04:06:36 +00:00
|
|
|
opts.on('-s', '--stdin', :NONE, 'Read input from standard input instead of an input file') do
|
2007-02-01 06:20:05 +00:00
|
|
|
@options[:input] = $stdin
|
|
|
|
end
|
|
|
|
|
2007-04-01 07:21:41 +00:00
|
|
|
opts.on('--trace', :NONE, 'Show a full traceback on error') do
|
|
|
|
@options[:trace] = true
|
|
|
|
end
|
|
|
|
|
2010-01-23 12:52:30 -08:00
|
|
|
if RbConfig::CONFIG['host_os'] =~ /mswin|windows/i
|
|
|
|
opts.on('--unix-newlines', 'Use Unix-style newlines in written files.') do
|
|
|
|
@options[:unix_newlines] = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-02-01 06:20:05 +00:00
|
|
|
opts.on_tail("-?", "-h", "--help", "Show this message") do
|
|
|
|
puts opts
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
|
|
|
|
opts.on_tail("-v", "--version", "Print version") do
|
2009-09-23 13:47:02 -07:00
|
|
|
puts("Haml/Sass #{::Haml.version[:string]}")
|
2007-02-01 06:20:05 +00:00
|
|
|
exit
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Processes the options set by the command-line arguments.
|
|
|
|
# In particular, sets `@options[:input]` and `@options[:output]`
|
|
|
|
# to appropriate IO streams.
|
|
|
|
#
|
|
|
|
# This is meant to be overridden by subclasses
|
|
|
|
# so they can run their respective programs.
|
2007-02-01 06:20:05 +00:00
|
|
|
def process_result
|
2007-12-08 02:41:59 +00:00
|
|
|
input, output = @options[:input], @options[:output]
|
2010-03-06 17:47:15 -08:00
|
|
|
args = @args.dup
|
|
|
|
input ||=
|
|
|
|
begin
|
|
|
|
filename = args.shift
|
|
|
|
@options[:filename] = filename
|
|
|
|
open_file(filename) || $stdin
|
|
|
|
end
|
|
|
|
output ||= open_file(args.shift, 'w') || $stdout
|
2007-12-08 02:41:59 +00:00
|
|
|
|
|
|
|
@options[:input], @options[:output] = input, output
|
|
|
|
end
|
2007-02-01 06:20:05 +00:00
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
private
|
|
|
|
|
2007-12-08 02:41:59 +00:00
|
|
|
def open_file(filename, flag = 'r')
|
|
|
|
return if filename.nil?
|
2010-01-23 12:52:30 -08:00
|
|
|
flag = 'wb' if @options[:unix_newlines] && flag == 'w'
|
2007-12-08 02:41:59 +00:00
|
|
|
File.open(filename, flag)
|
2007-02-01 06:20:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# An abstrac class that encapsulates the code
|
|
|
|
# specific to the `haml` and `sass` executables.
|
|
|
|
class HamlSass < Generic
|
|
|
|
# @param args [Array<String>] The command-line arguments
|
2007-12-08 05:08:51 +00:00
|
|
|
def initialize(args)
|
|
|
|
super
|
|
|
|
@options[:for_engine] = {}
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
protected
|
2007-02-01 06:20:05 +00:00
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Tells optparse how to parse the arguments
|
|
|
|
# available for the `haml` and `sass` executables.
|
|
|
|
#
|
|
|
|
# This is meant to be overridden by subclasses
|
|
|
|
# so they can add their own options.
|
|
|
|
#
|
|
|
|
# @param opts [OptionParser]
|
2007-02-01 06:20:05 +00:00
|
|
|
def set_opts(opts)
|
|
|
|
opts.banner = <<END
|
2007-12-08 02:41:59 +00:00
|
|
|
Usage: #{@name.downcase} [options] [INPUT] [OUTPUT]
|
2007-02-01 06:20:05 +00:00
|
|
|
|
|
|
|
Description:
|
|
|
|
Uses the #{@name} engine to parse the specified template
|
|
|
|
and outputs the result to the specified file.
|
|
|
|
|
|
|
|
Options:
|
|
|
|
END
|
2008-04-07 23:09:17 -07:00
|
|
|
|
2007-12-08 05:08:51 +00:00
|
|
|
opts.on('--rails RAILS_DIR', "Install Haml and Sass from the Gem to a Rails project") do |dir|
|
2007-03-07 09:09:16 +00:00
|
|
|
original_dir = dir
|
|
|
|
|
|
|
|
dir = File.join(dir, 'vendor', 'plugins')
|
|
|
|
|
|
|
|
unless File.exists?(dir)
|
|
|
|
puts "Directory #{dir} doesn't exist"
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
|
|
|
|
dir = File.join(dir, 'haml')
|
|
|
|
|
|
|
|
if File.exists?(dir)
|
2008-05-24 12:13:36 -07:00
|
|
|
print "Directory #{dir} already exists, overwrite [y/N]? "
|
|
|
|
exit if gets !~ /y/i
|
|
|
|
FileUtils.rm_rf(dir)
|
2007-03-07 09:09:16 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
begin
|
|
|
|
Dir.mkdir(dir)
|
|
|
|
rescue SystemCallError
|
|
|
|
puts "Cannot create #{dir}"
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
|
|
|
|
File.open(File.join(dir, 'init.rb'), 'w') do |file|
|
2007-11-24 22:41:16 +00:00
|
|
|
file << File.read(File.dirname(__FILE__) + "/../../init.rb")
|
2007-03-07 09:09:16 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
puts "Haml plugin added to #{original_dir}"
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
|
2007-12-11 10:20:42 +00:00
|
|
|
opts.on('-c', '--check', "Just check syntax, don't evaluate.") do
|
2008-04-17 17:24:50 -07:00
|
|
|
require 'stringio'
|
2007-12-11 10:20:42 +00:00
|
|
|
@options[:check_syntax] = true
|
|
|
|
@options[:output] = StringIO.new
|
|
|
|
end
|
|
|
|
|
2007-02-01 06:20:05 +00:00
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Processes the options set by the command-line arguments.
|
|
|
|
# In particular, sets `@options[:for_engine][:filename]` to the input filename
|
|
|
|
# and requires the appropriate file.
|
|
|
|
#
|
|
|
|
# This is meant to be overridden by subclasses
|
|
|
|
# so they can run their respective programs.
|
2007-02-01 06:20:05 +00:00
|
|
|
def process_result
|
|
|
|
super
|
2008-10-08 18:26:43 -07:00
|
|
|
@options[:for_engine][:filename] = @options[:filename] if @options[:filename]
|
2007-02-01 06:20:05 +00:00
|
|
|
require File.dirname(__FILE__) + "/../#{@name.downcase}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# The `sass` executable.
|
|
|
|
class Sass < HamlSass
|
|
|
|
# @param args [Array<String>] The command-line arguments
|
2007-02-01 06:20:05 +00:00
|
|
|
def initialize(args)
|
|
|
|
super
|
|
|
|
@name = "Sass"
|
2008-10-21 09:11:49 -07:00
|
|
|
@options[:for_engine][:load_paths] = ['.'] + (ENV['SASSPATH'] || '').split(File::PATH_SEPARATOR)
|
2007-02-01 06:20:05 +00:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
protected
|
|
|
|
|
|
|
|
# Tells optparse how to parse the arguments.
|
|
|
|
#
|
|
|
|
# @param opts [OptionParser]
|
2007-12-08 05:08:51 +00:00
|
|
|
def set_opts(opts)
|
|
|
|
super
|
|
|
|
|
2009-12-21 13:03:10 -08:00
|
|
|
opts.on('--scss',
|
|
|
|
'Use the CSS-superset SCSS syntax.') do
|
|
|
|
@options[:for_engine][:syntax] = :scss
|
|
|
|
end
|
2010-01-12 21:43:17 -08:00
|
|
|
opts.on('--watch', 'Watch files or directories for changes.',
|
|
|
|
'The location of the generated CSS can be set using a colon:',
|
|
|
|
' sass --watch input.sass:output.css',
|
|
|
|
' sass --watch input-dir:output-dir') do
|
2010-01-12 20:57:02 -08:00
|
|
|
@options[:watch] = true
|
|
|
|
end
|
2010-01-15 17:13:55 -08:00
|
|
|
opts.on('--update', 'Compile files or directories to CSS.',
|
|
|
|
'Locations are set like --watch.') do
|
|
|
|
@options[:update] = true
|
|
|
|
end
|
2007-12-08 05:08:51 +00:00
|
|
|
opts.on('-t', '--style NAME',
|
2008-03-30 09:42:32 -07:00
|
|
|
'Output style. Can be nested (default), compact, compressed, or expanded.') do |name|
|
2007-12-08 05:08:51 +00:00
|
|
|
@options[:for_engine][:style] = name.to_sym
|
|
|
|
end
|
2010-03-19 04:31:02 -07:00
|
|
|
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
|
|
|
|
end
|
2010-01-23 12:17:05 -08:00
|
|
|
opts.on('-l', '--line-numbers', '--line-comments',
|
|
|
|
'Emit comments in the generated CSS indicating the corresponding sass line.') do
|
|
|
|
@options[:for_engine][:line_numbers] = true
|
2008-08-09 09:39:49 -07:00
|
|
|
end
|
2008-10-16 16:23:45 -07:00
|
|
|
opts.on('-i', '--interactive',
|
|
|
|
'Run an interactive SassScript shell.') do
|
|
|
|
@options[:interactive] = true
|
|
|
|
end
|
2008-10-21 09:11:49 -07:00
|
|
|
opts.on('-I', '--load-path PATH', 'Add a sass import path.') do |path|
|
|
|
|
@options[:for_engine][:load_paths] << path
|
|
|
|
end
|
2010-01-27 00:28:32 +00:00
|
|
|
opts.on('--cache-location PATH', 'The path to put cached Sass files. Defaults to .sass-cache.') do |loc|
|
|
|
|
@options[:for_engine][:cache_location] = loc
|
2009-04-23 09:59:38 -07:00
|
|
|
end
|
|
|
|
opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
|
|
|
|
@options[:for_engine][:cache] = false
|
|
|
|
end
|
2007-12-08 05:08:51 +00:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Processes the options set by the command-line arguments,
|
|
|
|
# and runs the Sass compiler appropriately.
|
2007-02-01 06:20:05 +00:00
|
|
|
def process_result
|
2010-02-06 13:25:06 -08:00
|
|
|
if !@options[:update] && !@options[:watch] &&
|
|
|
|
@args.first && @args.first.include?(':')
|
2010-01-23 17:14:38 -08:00
|
|
|
if @args.size == 1
|
|
|
|
@args = @args.first.split(':', 2)
|
|
|
|
else
|
|
|
|
@options[:update] = true
|
|
|
|
end
|
2008-10-16 16:23:45 -07:00
|
|
|
end
|
|
|
|
|
2010-01-18 20:30:48 -08:00
|
|
|
return interactive if @options[:interactive]
|
|
|
|
return watch_or_update if @options[:watch] || @options[:update]
|
2007-02-01 06:20:05 +00:00
|
|
|
super
|
2007-12-08 04:06:36 +00:00
|
|
|
|
2009-12-24 11:30:27 -08:00
|
|
|
begin
|
|
|
|
input = @options[:input]
|
|
|
|
output = @options[:output]
|
|
|
|
|
2010-01-12 18:29:41 -08:00
|
|
|
@options[:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
|
2009-12-24 11:30:27 -08:00
|
|
|
tree =
|
|
|
|
if input.is_a?(File) && !@options[:check_syntax]
|
|
|
|
::Sass::Files.tree_for(input.path, @options[:for_engine])
|
|
|
|
else
|
|
|
|
# We don't need to do any special handling of @options[:check_syntax] here,
|
|
|
|
# because the Sass syntax checking happens alongside evaluation
|
|
|
|
# and evaluation doesn't actually evaluate any code anyway.
|
|
|
|
::Sass::Engine.new(input.read(), @options[:for_engine]).to_tree
|
|
|
|
end
|
|
|
|
|
|
|
|
input.close() if input.is_a?(File)
|
|
|
|
|
2010-02-21 16:41:48 -08:00
|
|
|
output.write(tree.render)
|
2009-12-24 11:30:27 -08:00
|
|
|
output.close() if output.is_a? File
|
|
|
|
rescue ::Sass::SyntaxError => e
|
|
|
|
raise e if @options[:trace]
|
2009-12-24 11:32:47 -08:00
|
|
|
raise e.sass_backtrace_str("standard input")
|
2009-12-24 11:30:27 -08:00
|
|
|
end
|
2007-02-01 06:20:05 +00:00
|
|
|
end
|
2010-01-18 20:30:48 -08:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def interactive
|
|
|
|
require 'sass'
|
|
|
|
require 'sass/repl'
|
|
|
|
::Sass::Repl.new(@options).run
|
|
|
|
end
|
|
|
|
|
|
|
|
def watch_or_update
|
|
|
|
require 'sass'
|
|
|
|
require 'sass/plugin'
|
2010-03-23 16:14:57 -07:00
|
|
|
::Sass::Plugin.options.merge! @options[:for_engine]
|
2010-01-23 13:55:19 -08:00
|
|
|
::Sass::Plugin.options[:unix_newlines] = @options[:unix_newlines]
|
|
|
|
|
2010-02-06 13:34:23 -08:00
|
|
|
if @args[1] && !@args[0].include?(':')
|
|
|
|
flag = @options[:update] ? "--update" : "--watch"
|
|
|
|
err =
|
|
|
|
if !File.exist?(@args[1])
|
|
|
|
"doesn't exist"
|
|
|
|
elsif @args[1] =~ /\.css$/
|
|
|
|
"is a CSS file"
|
|
|
|
end
|
|
|
|
raise <<MSG if err
|
|
|
|
File #{@args[1]} #{err}.
|
|
|
|
Did you mean: sass #{flag} #{@args[0]}:#{@args[1]}
|
|
|
|
MSG
|
|
|
|
end
|
|
|
|
|
2010-01-18 20:30:48 -08:00
|
|
|
dirs, files = @args.map {|name| name.split(':', 2)}.
|
|
|
|
map {|from, to| [from, to || from.gsub(/\..*?$/, '.css')]}.
|
|
|
|
partition {|i, _| File.directory? i}
|
|
|
|
::Sass::Plugin.options[:template_location] = dirs
|
|
|
|
|
|
|
|
::Sass::Plugin.on_updating_stylesheet do |_, css|
|
|
|
|
if File.exists? css
|
2010-01-18 21:09:36 -08:00
|
|
|
puts_action :overwrite, :yellow, css
|
2010-01-18 20:30:48 -08:00
|
|
|
else
|
2010-01-18 21:09:36 -08:00
|
|
|
puts_action :create, :green, css
|
2010-01-18 20:30:48 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-01-18 21:09:36 -08:00
|
|
|
::Sass::Plugin.on_creating_directory {|dirname| puts_action :directory, :green, dirname}
|
|
|
|
::Sass::Plugin.on_deleting_css {|filename| puts_action :delete, :yellow, filename}
|
2010-01-18 20:30:48 -08:00
|
|
|
::Sass::Plugin.on_compilation_error do |error, _, _|
|
2010-02-06 13:34:23 -08:00
|
|
|
raise error unless error.is_a?(::Sass::SyntaxError)
|
2010-01-18 21:09:36 -08:00
|
|
|
puts_action :error, :red, "#{error.sass_filename} (Line #{error.sass_line}: #{error.message})"
|
2010-01-18 20:30:48 -08:00
|
|
|
end
|
|
|
|
|
2010-01-18 21:20:55 -08:00
|
|
|
if @options[:update]
|
|
|
|
::Sass::Plugin.update_stylesheets(files)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
puts ">>> Sass is watching for changes. Press Ctrl-C to stop."
|
|
|
|
|
2010-01-18 20:30:48 -08:00
|
|
|
::Sass::Plugin.on_template_modified {|template| puts ">>> Change detected to: #{template}"}
|
|
|
|
::Sass::Plugin.on_template_created {|template| puts ">>> New template detected: #{template}"}
|
|
|
|
::Sass::Plugin.on_template_deleted {|template| puts ">>> Deleted template detected: #{template}"}
|
|
|
|
|
|
|
|
::Sass::Plugin.watch(files)
|
|
|
|
end
|
|
|
|
|
2010-01-18 21:09:36 -08:00
|
|
|
# @private
|
|
|
|
COLORS = { :red => 31, :green => 32, :yellow => 33 }
|
|
|
|
|
|
|
|
def puts_action(name, color, arg)
|
2010-01-23 17:10:09 -08:00
|
|
|
printf color(color, "%11s %s\n"), name, arg
|
2010-01-18 21:09:36 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
def color(color, str)
|
|
|
|
raise "[BUG] Unrecognized color #{color}" unless COLORS[color]
|
|
|
|
|
|
|
|
# Almost any real Unix terminal will support color,
|
|
|
|
# so we just filter for Windows terms (which don't set TERM)
|
|
|
|
# and not-real terminals, which aren't ttys.
|
|
|
|
return str if ENV["TERM"].empty? || !STDOUT.tty?
|
2010-01-23 17:10:09 -08:00
|
|
|
return "\e[#{COLORS[color]}m#{str}\e[0m"
|
2010-01-18 20:30:48 -08:00
|
|
|
end
|
2007-02-01 06:20:05 +00:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# The `haml` executable.
|
|
|
|
class Haml < HamlSass
|
|
|
|
# @param args [Array<String>] The command-line arguments
|
2007-02-01 06:20:05 +00:00
|
|
|
def initialize(args)
|
|
|
|
super
|
|
|
|
@name = "Haml"
|
2008-08-31 15:41:32 -07:00
|
|
|
@options[:requires] = []
|
|
|
|
@options[:load_paths] = []
|
2007-02-01 06:20:05 +00:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Tells optparse how to parse the arguments.
|
|
|
|
#
|
|
|
|
# @param opts [OptionParser]
|
2008-02-29 10:52:09 -08:00
|
|
|
def set_opts(opts)
|
|
|
|
super
|
|
|
|
|
|
|
|
opts.on('-t', '--style NAME',
|
|
|
|
'Output style. Can be indented (default) or ugly.') do |name|
|
|
|
|
@options[:for_engine][:ugly] = true if name.to_sym == :ugly
|
|
|
|
end
|
2008-02-29 10:57:41 -08:00
|
|
|
|
|
|
|
opts.on('-f', '--format NAME',
|
|
|
|
'Output format. Can be xhtml (default), html4, or html5.') do |name|
|
2008-02-29 11:00:03 -08:00
|
|
|
@options[:for_engine][:format] = name.to_sym
|
2008-02-29 10:57:41 -08:00
|
|
|
end
|
2008-04-07 23:09:17 -07:00
|
|
|
|
2008-03-14 16:39:19 -07:00
|
|
|
opts.on('-e', '--escape-html',
|
|
|
|
'Escape HTML characters (like ampersands and angle brackets) by default.') do
|
|
|
|
@options[:for_engine][:escape_html] = true
|
|
|
|
end
|
2008-08-31 15:41:32 -07:00
|
|
|
|
2009-07-30 20:38:04 +01:00
|
|
|
opts.on('-q', '--double-quote-attributes',
|
|
|
|
'Set attribute wrapper to double-quotes (default is single).') do
|
|
|
|
@options[:for_engine][:attr_wrapper] = '"'
|
|
|
|
end
|
|
|
|
|
2008-08-31 15:41:32 -07:00
|
|
|
opts.on('-r', '--require FILE', "Same as 'ruby -r'.") do |file|
|
|
|
|
@options[:requires] << file
|
|
|
|
end
|
|
|
|
|
|
|
|
opts.on('-I', '--load-path PATH', "Same as 'ruby -I'.") do |path|
|
|
|
|
@options[:load_paths] << path
|
|
|
|
end
|
2008-12-13 15:33:29 -08:00
|
|
|
|
|
|
|
opts.on('--debug', "Print out the precompiled Ruby source.") do
|
|
|
|
@options[:debug] = true
|
|
|
|
end
|
2008-02-29 10:52:09 -08:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Processes the options set by the command-line arguments,
|
|
|
|
# and runs the Haml compiler appropriately.
|
2007-02-01 06:20:05 +00:00
|
|
|
def process_result
|
|
|
|
super
|
|
|
|
input = @options[:input]
|
|
|
|
output = @options[:output]
|
|
|
|
|
|
|
|
template = input.read()
|
|
|
|
input.close() if input.is_a? File
|
2007-12-08 04:06:36 +00:00
|
|
|
|
|
|
|
begin
|
2007-12-11 10:20:42 +00:00
|
|
|
engine = ::Haml::Engine.new(template, @options[:for_engine])
|
|
|
|
if @options[:check_syntax]
|
|
|
|
puts "Syntax OK"
|
|
|
|
return
|
|
|
|
end
|
2008-08-31 15:41:32 -07:00
|
|
|
|
|
|
|
@options[:load_paths].each {|p| $LOAD_PATH << p}
|
|
|
|
@options[:requires].each {|f| require f}
|
2008-12-13 15:33:29 -08:00
|
|
|
|
|
|
|
if @options[:debug]
|
|
|
|
puts engine.precompiled
|
|
|
|
puts '=' * 100
|
|
|
|
end
|
|
|
|
|
2007-12-11 10:20:42 +00:00
|
|
|
result = engine.to_html
|
2007-12-08 04:06:36 +00:00
|
|
|
rescue Exception => e
|
|
|
|
raise e if @options[:trace]
|
|
|
|
|
|
|
|
case e
|
2007-12-26 02:22:23 +00:00
|
|
|
when ::Haml::SyntaxError; raise "Syntax error on line #{get_line e}: #{e.message}"
|
2008-04-19 10:07:40 -07:00
|
|
|
when ::Haml::Error; raise "Haml error on line #{get_line e}: #{e.message}"
|
2007-12-08 04:06:36 +00:00
|
|
|
else raise "Exception on line #{get_line e}: #{e.message}\n Use --trace for backtrace."
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-02-01 06:20:05 +00:00
|
|
|
output.write(result)
|
|
|
|
output.close() if output.is_a? File
|
|
|
|
end
|
|
|
|
end
|
2007-02-02 07:36:58 +00:00
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# The `html2haml` executable.
|
|
|
|
class HTML2Haml < Generic
|
|
|
|
# @param args [Array<String>] The command-line arguments
|
2007-04-01 08:45:39 +00:00
|
|
|
def initialize(args)
|
|
|
|
super
|
2007-04-10 00:29:57 +00:00
|
|
|
@module_opts = {}
|
2007-04-01 08:45:39 +00:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Tells optparse how to parse the arguments.
|
|
|
|
#
|
|
|
|
# @param opts [OptionParser]
|
2007-02-02 07:36:58 +00:00
|
|
|
def set_opts(opts)
|
|
|
|
opts.banner = <<END
|
2007-12-08 02:41:59 +00:00
|
|
|
Usage: html2haml [options] [INPUT] [OUTPUT]
|
2007-02-02 07:36:58 +00:00
|
|
|
|
|
|
|
Description: Transforms an HTML file into corresponding Haml code.
|
|
|
|
|
|
|
|
Options:
|
|
|
|
END
|
|
|
|
|
2009-10-04 21:15:16 -07:00
|
|
|
opts.on('-e', '--erb', 'Parse ERb tags.') do
|
|
|
|
@module_opts[:erb] = true
|
2007-04-10 00:29:57 +00:00
|
|
|
end
|
|
|
|
|
2009-10-04 21:15:16 -07:00
|
|
|
opts.on('--no-erb', "Don't parse ERb tags.") do
|
|
|
|
@options[:no_erb] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
opts.on('-r', '--rhtml', 'Deprecated; same as --erb.') do
|
|
|
|
@module_opts[:erb] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
opts.on('--no-rhtml', "Deprecated; same as --no-erb.") do
|
|
|
|
@options[:no_erb] = true
|
2008-08-03 00:27:12 -04:00
|
|
|
end
|
|
|
|
|
2007-12-11 10:30:14 +00:00
|
|
|
opts.on('-x', '--xhtml', 'Parse the input using the more strict XHTML parser.') do
|
|
|
|
@module_opts[:xhtml] = true
|
|
|
|
end
|
|
|
|
|
2007-02-02 07:36:58 +00:00
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Processes the options set by the command-line arguments,
|
|
|
|
# and runs the HTML compiler appropriately.
|
2007-02-02 07:36:58 +00:00
|
|
|
def process_result
|
|
|
|
super
|
|
|
|
|
2009-10-23 12:30:22 -07:00
|
|
|
require 'haml/html'
|
|
|
|
|
2007-02-02 07:36:58 +00:00
|
|
|
input = @options[:input]
|
|
|
|
output = @options[:output]
|
|
|
|
|
2009-10-04 21:15:16 -07:00
|
|
|
@module_opts[:erb] ||= input.respond_to?(:path) && input.path =~ /\.(rhtml|erb)$/
|
|
|
|
@module_opts[:erb] &&= @options[:no_erb] != false
|
2008-08-03 00:27:12 -04:00
|
|
|
|
2007-04-10 00:29:57 +00:00
|
|
|
output.write(::Haml::HTML.new(input, @module_opts).render)
|
2009-09-23 15:54:46 -07:00
|
|
|
rescue ::Haml::Error => e
|
|
|
|
raise "#{e.is_a?(::Haml::SyntaxError) ? "Syntax error" : "Error"} on line " +
|
|
|
|
"#{get_line e}: #{e.message}"
|
2009-10-23 12:30:22 -07:00
|
|
|
rescue LoadError => err
|
|
|
|
dep = err.message.scan(/^no such file to load -- (.*)/)[0]
|
|
|
|
raise err if @options[:trace] || dep.nil? || dep.empty?
|
|
|
|
$stderr.puts "Required dependency #{dep} not found!\n Use --trace for backtrace."
|
|
|
|
exit 1
|
2007-02-02 07:36:58 +00:00
|
|
|
end
|
|
|
|
end
|
2007-04-01 10:03:12 +00:00
|
|
|
|
2010-02-21 17:07:27 -08:00
|
|
|
# The `sass-convert` executable.
|
|
|
|
class SassConvert < Generic
|
2009-04-29 15:53:47 -07:00
|
|
|
# @param args [Array<String>] The command-line arguments
|
2007-04-01 10:03:12 +00:00
|
|
|
def initialize(args)
|
|
|
|
super
|
2010-02-21 17:07:27 -08:00
|
|
|
@options[:for_tree] = {}
|
2010-02-21 17:17:41 -08:00
|
|
|
@options[:for_engine] = {}
|
2007-04-01 10:03:12 +00:00
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Tells optparse how to parse the arguments.
|
|
|
|
#
|
|
|
|
# @param opts [OptionParser]
|
2007-04-01 10:03:12 +00:00
|
|
|
def set_opts(opts)
|
|
|
|
opts.banner = <<END
|
2010-02-21 17:07:27 -08:00
|
|
|
Usage: sass-convert [options] [INPUT] [OUTPUT]
|
2007-04-01 10:03:12 +00:00
|
|
|
|
2010-02-21 17:07:27 -08:00
|
|
|
Description:
|
|
|
|
Converts between CSS, Sass, and SCSS files.
|
|
|
|
E.g. converts from SCSS to Sass,
|
|
|
|
or converts from CSS to SCSS (adding appropriate nesting).
|
2007-04-01 10:03:12 +00:00
|
|
|
|
|
|
|
Options:
|
|
|
|
END
|
|
|
|
|
2010-02-21 17:15:09 -08:00
|
|
|
opts.on('-F', '--from FORMAT',
|
2010-03-29 19:34:08 -07:00
|
|
|
'The format to convert from. Can be css, scss, sass, or sass2.',
|
|
|
|
'sass2 is the same as sass, but updates more old syntax to new.',
|
2010-02-21 18:21:38 -08:00
|
|
|
'By default, this is inferred from the input filename.',
|
|
|
|
'If there is none, defaults to css.') do |name|
|
2010-02-21 17:07:27 -08:00
|
|
|
@options[:from] = name.downcase.to_sym
|
2008-04-29 13:14:43 -07:00
|
|
|
end
|
|
|
|
|
2010-02-21 17:15:09 -08:00
|
|
|
opts.on('-T', '--to FORMAT',
|
2010-02-21 17:07:27 -08:00
|
|
|
'The format to convert to. Can be scss or sass.',
|
2010-02-21 18:21:38 -08:00
|
|
|
'By default, this is inferred from the output filename.',
|
|
|
|
'If there is none, defaults to sass.') do |name|
|
2010-02-21 17:07:27 -08:00
|
|
|
@options[:to] = name.downcase.to_sym
|
|
|
|
end
|
|
|
|
|
2010-03-06 18:16:46 -08:00
|
|
|
opts.on('-i', '--in-place',
|
|
|
|
'Convert a file to its own syntax.',
|
|
|
|
'This can be used to update some deprecated syntax.') do
|
|
|
|
@options[:in_place] = true
|
|
|
|
end
|
|
|
|
|
2010-02-21 17:07:27 -08:00
|
|
|
opts.on('--old', 'Output the old-style ":prop val" property syntax.',
|
|
|
|
'Only meaningful when generating Sass.') do
|
|
|
|
@options[:for_tree][:old] = true
|
|
|
|
end
|
2009-06-19 02:57:01 -07:00
|
|
|
|
2010-02-21 17:14:29 -08:00
|
|
|
opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
|
|
|
|
@options[:for_engine][:cache] = false
|
|
|
|
end
|
|
|
|
|
2007-04-01 10:03:12 +00:00
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2009-04-29 15:53:47 -07:00
|
|
|
# Processes the options set by the command-line arguments,
|
|
|
|
# and runs the CSS compiler appropriately.
|
2007-04-01 10:03:12 +00:00
|
|
|
def process_result
|
2010-03-06 18:16:46 -08:00
|
|
|
require 'sass'
|
2007-04-01 10:03:12 +00:00
|
|
|
super
|
|
|
|
|
|
|
|
input = @options[:input]
|
|
|
|
output = @options[:output]
|
2010-03-06 18:16:46 -08:00
|
|
|
output = input if @options[:in_place]
|
2007-04-01 10:03:12 +00:00
|
|
|
|
2010-02-21 17:10:42 -08:00
|
|
|
if input.is_a?(File)
|
|
|
|
@options[:from] ||=
|
|
|
|
case input.path
|
|
|
|
when /\.scss$/; :scss
|
|
|
|
when /\.sass$/; :sass
|
|
|
|
when /\.css$/; :css
|
|
|
|
end
|
2010-03-06 18:16:46 -08:00
|
|
|
elsif @options[:in_place]
|
|
|
|
raise "Error: the --in-place option requires a filename."
|
2010-02-21 17:10:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
if output.is_a?(File)
|
|
|
|
@options[:to] ||=
|
2010-02-21 17:33:58 -08:00
|
|
|
case output.path
|
2010-02-21 17:10:42 -08:00
|
|
|
when /\.scss$/; :scss
|
|
|
|
when /\.sass$/; :sass
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-03-29 19:34:08 -07:00
|
|
|
if @options[:from] == :sass2
|
|
|
|
@options[:from] = :sass
|
|
|
|
@options[:for_engine][:sass2] = true
|
|
|
|
end
|
|
|
|
|
2010-02-21 18:21:38 -08:00
|
|
|
@options[:from] ||= :css
|
|
|
|
@options[:to] ||= :sass
|
2010-02-21 18:26:21 -08:00
|
|
|
@options[:for_engine][:syntax] = @options[:from]
|
2010-02-21 18:21:38 -08:00
|
|
|
|
2010-02-21 17:07:27 -08:00
|
|
|
out =
|
|
|
|
if @options[:from] == :css
|
|
|
|
require 'sass/css'
|
|
|
|
::Sass::CSS.new(input.read, @options[:for_tree]).render(@options[:to])
|
|
|
|
else
|
2010-02-21 17:12:46 -08:00
|
|
|
if input.is_a?(File)
|
2010-02-21 17:14:29 -08:00
|
|
|
::Sass::Files.tree_for(input.path, @options[:for_engine])
|
2010-02-21 17:12:46 -08:00
|
|
|
else
|
2010-02-21 17:14:29 -08:00
|
|
|
::Sass::Engine.new(input.read, @options[:for_engine]).to_tree
|
2010-02-21 17:12:46 -08:00
|
|
|
end.send("to_#{@options[:to]}", @options[:for_tree])
|
2010-02-21 17:07:27 -08:00
|
|
|
end
|
|
|
|
|
2010-03-06 18:16:46 -08:00
|
|
|
output = File.open(input.path, 'w') if @options[:in_place]
|
2010-02-21 17:07:27 -08:00
|
|
|
output.write(out)
|
2009-09-23 14:22:24 -07:00
|
|
|
rescue ::Sass::SyntaxError => e
|
|
|
|
raise e if @options[:trace]
|
|
|
|
raise "Syntax error on line #{get_line e}: #{e.message}\n Use --trace for backtrace"
|
2007-04-01 10:03:12 +00:00
|
|
|
end
|
|
|
|
end
|
2007-02-01 06:20:05 +00:00
|
|
|
end
|
|
|
|
end
|