Last active
August 29, 2015 14:21
-
-
Save dux/d898ad776593e571587b to your computer and use it in GitHub Desktop.
Assets parser, raw and uninteligent. parses coffee, less, csss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# gem install sass | |
# npm install -g clean-css | |
# npm install -g uglify-js | |
# less, scss, coffee | |
require 'colorize' | |
require 'pathname' | |
require 'json' | |
require 'listen' | |
require 'optparse' | |
class Asset | |
def r(what) | |
puts what.red | |
exit | |
end | |
def initialize(file, opts={}) | |
ext = file.split('.').reverse[0].to_sym | |
unless [:js, :css, :coffee, :less, :scss, :sass, :haml, :coffe].index(ext) | |
puts "Unsuported file type #{file}" | |
exit | |
end | |
@file = file | |
@dir = Pathname.new(file).dirname | |
@opts = opts | |
@opts[:root] ||= './' | |
@opts[:cache] ||= '.assets_cache' | |
Dir.mkdir(@opts[:cache]) unless Dir.exists?(@opts[:cache]) | |
Dir.mkdir('./public/assets') unless Dir.exists?('./public/assets') | |
puts "Wathing #{file.yellow}" | |
end | |
def cache_file(original_file, ext_only=false) | |
file = original_file.sub(/\.root\./, '.') | |
file.sub!(/\.(\w+)$/,'') | |
original_ext = $1.to_sym | |
ext = [:js, :coffee].index(original_ext) ? :js : :css | |
return ext if ext_only | |
file.sub!(/\.\w+$/,'') | |
"#{@opts[:cache]}/#{file.gsub(/\/+/,'-')}.#{ext}" | |
end | |
def run | |
@time = Time.new | |
@compile_needed = false | |
@original_file_names = [] | |
data = File.read(@file) | |
ret = [] | |
files_to_process = [] | |
lines = data.split("\n") | |
lines.each_with_index do |line, i| | |
elms = line.split(/\s+/) | |
if ['//=','#='].index(elms[0]) | |
directive, source = elms[1], elms[2] | |
to_load = (@dir + source).to_s | |
if directive == 'require_tree' || source.index('*') | |
to_load += '/*' unless to_load.index('*') | |
files = Dir[to_load.sub(/\/+/,'/')] | |
exit puts "No files in #{source}" unless files.length > 0 | |
for file in files.sort | |
files_to_process.push compile(file) | |
end | |
else | |
files_to_process.push compile(to_load) | |
end | |
else | |
ret.push line | |
end | |
end | |
master_file_cache = "#{@opts[:cache]}/#{@file.gsub('/','-')}" | |
public_file = 'public/assets/'[email protected]('assets/')[1].sub(/\.[^\/]+$/,'').gsub('/','-')+".#{cache_file(@file, :ext)}" | |
@compile_needed = true if !File.exist?(master_file_cache) || !File.exist?(public_file) || File.mtime(public_file) < File.mtime(@file) | |
if @compile_needed | |
data = ret.join("\n") | |
File.open(master_file_cache, 'w') { |f| f.write(data) } | |
master_file_compiled = compile(master_file_cache) | |
public_data = [] | |
@original_file_names.pop | |
@original_file_names.push @file | |
public_data.push "/*\n Original files in use\n"+@original_file_names.map{ |el| " - #{el}"}.join("\n") +"\n*/" | |
for file in files_to_process | |
public_data.push "\n/* File: #{file.split('/')[1].gsub('-','/')} */\n"+File.read(file) | |
end | |
public_data.push File.read master_file_compiled | |
File.open(public_file, 'w') { |f| f.write(public_data.join("\n\n")) } | |
puts " compiled to #{public_file.green} in #{speed}" | |
if @opts[:compress] | |
if public_file.index('.css') | |
`cleancss --s0 '#{public_file_location}' -o '#{public_file_location}'` | |
else | |
`uglifyjs --screw-ie8 '#{public_file}' > #{public_file}.tmp` | |
`rm '#{public_file}'` | |
`mv '#{public_file}.tmp' '#{public_file}'` | |
end | |
end | |
end | |
public_file | |
end | |
def speed | |
ms = ((Time.new-@time)*1000).ceil | |
return "#{ms.to_s.yellow} ms" if ms < 1000 | |
ms.to_s.sub(/(\d)(\d{3})$/, '\1.\2').to_s.yellow + ' sec' | |
end | |
def compile(file, cache=nil) | |
@original_file_names.push file | |
ext = file.split('.').reverse[0].to_sym | |
cache ||= cache_file(file) | |
if [:scss, :sass].index(ext) | |
do_compile = false | |
do_compile = true unless File.exist?(cache) | |
if File.exists?("#{cache}.map") | |
hash = JSON.parse File.read("#{cache}.map") | |
files = hash['sources'].map{ |el| el.sub(/^\.\./,'.') } + [file] | |
cache_time = File.mtime(cache) | |
for el in files | |
next if do_compile | |
do_compile = true if !File.exist?(el) || cache_time < File.mtime(el) | |
end | |
else | |
do_compile = true | |
end | |
if do_compile | |
`scss -t compact '#{file}' --cache-location '.assets_cache' '#{cache}'` | |
@compile_needed = true | |
end | |
return cache | |
end | |
ext = file.split('.').reverse[0].to_sym | |
return file if [:js, :css].index(ext) | |
if !File.exist?(cache) || File.mtime(cache) < File.mtime(file) | |
@compile_needed = true | |
case ext | |
when :less | |
`lessc '#{file}' > '#{cache}'` | |
when :haml | |
`haml '#{file}' > '#{cache}'` | |
when :coffee | |
`coffee -p -c '#{file}' > '#{cache}'`; | |
else | |
return file | |
end | |
end | |
cache | |
end | |
end | |
options = {} | |
OptionParser.new do |opts| | |
opts.banner = "by twitter.com/@dux in 2015\nUsage: assets.rb [options]" | |
opts.banner += %[\nExamples: (File has to have #{'.root'.red} extension for tool to work)] | |
opts.banner += "\n assets.rb -f app/assets/admin/index#{'.root'.red}.coffee" | |
opts.banner += "\n assets.rb -l # live mode" | |
opts.banner += "\n assets.rb -c # compile all for production" | |
# opts.on("-h", "--help", "This help") { |v| options[:help] = v } | |
opts.on("-v", "--version", "Version") { |v| options[:version] = v } | |
opts.on("-c", "--compress", "Compress JS and CSS files") { |v| options[:compress] = v } | |
opts.on("-l", "--live", "Live reload root folders") { |v| options[:live] = v } | |
opts.on("-fFILE", "--file=FILE", "Compile single file") { |v| options[:file] = v } | |
end.parse! | |
if options[:version] | |
puts "0.0.1".yellow | |
exit | |
end | |
if options[:file] | |
Asset.new(options[:file], options).run | |
exit | |
end | |
files = `find app/assets | grep .root`.split("\n") | |
Dir['public/assets/*'].map{ |el| File.unlink(el) } | |
runner = Proc.new do | |
for file in files | |
Asset.new(file, options).run | |
end | |
end | |
runner.call | |
listener = Listen.to('./app/assets') do |modified, added, removed| | |
file = (modified || added || removed)[0].split('/app/', 2)[1] | |
puts "\nEvent on #{file.yellow}" | |
runner.call | |
end | |
listener.start | |
if options[:live] | |
sleep | |
else | |
puts 'Exiting, use -l option for live listening' | |
end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
by twitter.com/@dux in 2015 | |
Usage: assets.rb [options] | |
Examples: (File has to have .root extension for tool to work) | |
assets.rb -f app/assets/admin/index.root.coffee | |
assets.rb -l # live mode | |
assets.rb -c # compile all for production | |
-v, --version Version | |
-c, --compress Compress JS and CSS files | |
-l, --live Live reload root folders | |
-f, --file=FILE Compile single file | |
//= req foo/bar.js | |
#= req foo/baz.coffee | |
//= req foo/bar/* | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment