assets_booster 0.0.1

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.
@@ -0,0 +1,22 @@
1
+ var jsp = require("./parse-js"),
2
+ pro = require("./process"),
3
+ slice = jsp.slice,
4
+ member = jsp.member,
5
+ PRECEDENCE = jsp.PRECEDENCE,
6
+ OPERATORS = jsp.OPERATORS;
7
+
8
+ function ast_squeeze_more(ast) {
9
+ var w = pro.ast_walker(), walk = w.walk;
10
+ return w.with_walkers({
11
+ "call": function(expr, args) {
12
+ if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
13
+ // foo.toString() ==> foo+""
14
+ return [ "binary", "+", expr[1], [ "string", "" ]];
15
+ }
16
+ }
17
+ }, function() {
18
+ return walk(ast);
19
+ });
20
+ };
21
+
22
+ exports.ast_squeeze_more = ast_squeeze_more;
@@ -0,0 +1,203 @@
1
+ #! /usr/bin/env node
2
+ // -*- js2 -*-
3
+
4
+ global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
5
+ var fs = require("fs"),
6
+ jsp = require("./parse-js"),
7
+ pro = require("./process");
8
+
9
+ var options = {
10
+ ast: false,
11
+ mangle: true,
12
+ mangle_toplevel: false,
13
+ squeeze: true,
14
+ make_seqs: true,
15
+ dead_code: true,
16
+ beautify: false,
17
+ verbose: false,
18
+ show_copyright: true,
19
+ out_same_file: false,
20
+ max_line_length: 32 * 1024,
21
+ unsafe: false,
22
+ reserved_names: null,
23
+ beautify_options: {
24
+ indent_level: 4,
25
+ indent_start: 0,
26
+ quote_keys: false,
27
+ space_colon: false
28
+ },
29
+ output: true // stdout
30
+ };
31
+
32
+ var args = jsp.slice(process.argv, 2);
33
+ var filename;
34
+
35
+ out: while (args.length > 0) {
36
+ var v = args.shift();
37
+ switch (v) {
38
+ case "-b":
39
+ case "--beautify":
40
+ options.beautify = true;
41
+ break;
42
+ case "-i":
43
+ case "--indent":
44
+ options.beautify_options.indent_level = args.shift();
45
+ break;
46
+ case "-q":
47
+ case "--quote-keys":
48
+ options.beautify_options.quote_keys = true;
49
+ break;
50
+ case "-mt":
51
+ case "--mangle-toplevel":
52
+ options.mangle_toplevel = true;
53
+ break;
54
+ case "--no-mangle":
55
+ case "-nm":
56
+ options.mangle = false;
57
+ break;
58
+ case "--no-squeeze":
59
+ case "-ns":
60
+ options.squeeze = false;
61
+ break;
62
+ case "--no-seqs":
63
+ options.make_seqs = false;
64
+ break;
65
+ case "--no-dead-code":
66
+ options.dead_code = false;
67
+ break;
68
+ case "--no-copyright":
69
+ case "-nc":
70
+ options.show_copyright = false;
71
+ break;
72
+ case "-o":
73
+ case "--output":
74
+ options.output = args.shift();
75
+ break;
76
+ case "--overwrite":
77
+ options.out_same_file = true;
78
+ break;
79
+ case "-v":
80
+ case "--verbose":
81
+ options.verbose = true;
82
+ break;
83
+ case "--ast":
84
+ options.ast = true;
85
+ break;
86
+ case "--unsafe":
87
+ options.unsafe = true;
88
+ break;
89
+ case "--max-line-len":
90
+ options.max_line_length = parseInt(args.shift(), 10);
91
+ break;
92
+ case "--reserved-names":
93
+ options.reserved_names = args.shift().split(",");
94
+ break;
95
+ default:
96
+ filename = v;
97
+ break out;
98
+ }
99
+ }
100
+
101
+ if (options.verbose) {
102
+ pro.set_logger(function(msg){
103
+ sys.debug(msg);
104
+ });
105
+ }
106
+
107
+ if (filename) {
108
+ fs.readFile(filename, "utf8", function(err, text){
109
+ if (err) throw err;
110
+ output(squeeze_it(text));
111
+ });
112
+ } else {
113
+ var stdin = process.openStdin();
114
+ stdin.setEncoding("utf8");
115
+ var text = "";
116
+ stdin.on("data", function(chunk){
117
+ text += chunk;
118
+ });
119
+ stdin.on("end", function() {
120
+ output(squeeze_it(text));
121
+ });
122
+ }
123
+
124
+ function output(text) {
125
+ var out;
126
+ if (options.out_same_file && filename)
127
+ options.output = filename;
128
+ if (options.output === true) {
129
+ out = process.stdout;
130
+ } else {
131
+ out = fs.createWriteStream(options.output, {
132
+ flags: "w",
133
+ encoding: "utf8",
134
+ mode: 0644
135
+ });
136
+ }
137
+ out.write(text);
138
+ if (options.output !== true) {
139
+ out.end();
140
+ }
141
+ };
142
+
143
+ // --------- main ends here.
144
+
145
+ function show_copyright(comments) {
146
+ var ret = "";
147
+ for (var i = 0; i < comments.length; ++i) {
148
+ var c = comments[i];
149
+ if (c.type == "comment1") {
150
+ ret += "//" + c.value + "\n";
151
+ } else {
152
+ ret += "/*" + c.value + "*/";
153
+ }
154
+ }
155
+ return ret;
156
+ };
157
+
158
+ function squeeze_it(code) {
159
+ var result = "";
160
+ if (options.show_copyright) {
161
+ var tok = jsp.tokenizer(code), c;
162
+ c = tok();
163
+ result += show_copyright(c.comments_before);
164
+ }
165
+ try {
166
+ var ast = time_it("parse", function(){ return jsp.parse(code); });
167
+ if (options.mangle) ast = time_it("mangle", function(){
168
+ return pro.ast_mangle(ast, {
169
+ toplevel: options.mangle_toplevel,
170
+ except: options.reserved_names
171
+ });
172
+ });
173
+ if (options.squeeze) ast = time_it("squeeze", function(){
174
+ ast = pro.ast_squeeze(ast, {
175
+ make_seqs : options.make_seqs,
176
+ dead_code : options.dead_code,
177
+ keep_comps : !options.unsafe
178
+ });
179
+ if (options.unsafe)
180
+ ast = pro.ast_squeeze_more(ast);
181
+ return ast;
182
+ });
183
+ if (options.ast)
184
+ return sys.inspect(ast, null, null);
185
+ result += time_it("generate", function(){ return pro.gen_code(ast, options.beautify && options.beautify_options) });
186
+ if (!options.beautify && options.max_line_length) {
187
+ result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
188
+ }
189
+ return result;
190
+ } catch(ex) {
191
+ sys.debug(ex.stack);
192
+ sys.debug(sys.inspect(ex));
193
+ sys.debug(JSON.stringify(ex));
194
+ }
195
+ };
196
+
197
+ function time_it(name, cont) {
198
+ if (!options.verbose)
199
+ return cont();
200
+ var t1 = new Date().getTime();
201
+ try { return cont(); }
202
+ finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
203
+ };
@@ -0,0 +1,17 @@
1
+ module AssetsBooster
2
+ module Compiler
3
+ class Rainpress
4
+ def self.name
5
+ 'Rainpress'
6
+ end
7
+
8
+ def self.compile(css)
9
+ require 'rainpress'
10
+ ::Rainpress.compress(css)
11
+ rescue LoadError => e
12
+ raise "To use the Rainpress CSS Compressor, please install the rainpress gem first"
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,19 @@
1
+ module AssetsBooster
2
+ module Compiler
3
+ class Uglify
4
+ def self.name
5
+ 'UglifyJS running on Node.js'
6
+ end
7
+
8
+ def self.compile(code)
9
+ raise CompileError.new("You need to install node.js in order to compile using UglifyJS.") unless %x[which node].length > 1
10
+ IO.popen("cd #{Pathname.new(File.join(File.dirname(__FILE__),'node-js')).realpath} && node uglify.js", "r+") do |io|
11
+ io.write(code)
12
+ io.close_write
13
+ io.read
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,104 @@
1
+ require 'yaml'
2
+ module AssetsBooster
3
+ class Configuration
4
+ cattr_accessor :filename
5
+ cattr_accessor :config
6
+
7
+ self.filename = File.join(Rails.root, "config", "assets_booster.yml")
8
+
9
+ def self.load
10
+ self.config = YAML.load_file(filename)
11
+ AssetsBooster::Packager.packages = {}
12
+ config['packages'].each_pair do |type, packages|
13
+ type = type.to_sym
14
+ AssetsBooster::Packager.packages[type] = {}
15
+ packages.each_pair do |name, assets|
16
+ AssetsBooster::Packager.packages[type][name] = get_package(type).new(name, assets)
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.asset_path(file)
22
+ File.join(Rails.root, "public", file)
23
+ end
24
+
25
+ def self.compiler_for_type(type)
26
+ get_compiler(config['options'][type.to_s]['compiler'])
27
+ end
28
+
29
+ def self.defaults
30
+ {
31
+ 'packages' => {
32
+ 'javascript' => {},
33
+ 'stylesheet' => {},
34
+ },
35
+ 'options' => {
36
+ 'compiler' => {
37
+ 'javascript' => {
38
+ 'name' => "closure",
39
+ 'options' => "",
40
+ },
41
+ 'stylesheet' => {
42
+ 'name' => "rainpress",
43
+ 'options' => "",
44
+ }
45
+ },
46
+ 'environments' => %w(staging production),
47
+ }
48
+ }
49
+ end
50
+
51
+ def self.create
52
+ if File.exists?(filename)
53
+ AssetsBooster.log "#{filename} already exists. Aborting task..."
54
+ return
55
+ end
56
+
57
+ config = defaults
58
+ config['packages']['javascript'] = {
59
+ "base" => file_list("#{Rails.root}/public/javascripts", "js"),
60
+ }
61
+ config['packages']['stylesheet'] = {
62
+ "base" => file_list("#{Rails.root}/public/stylesheets", "css"),
63
+ }
64
+
65
+ File.open(filename, "w") do |out|
66
+ YAML.dump(config, out)
67
+ end
68
+ self.config = config
69
+
70
+ AssetsBooster.log("#{filename} example file created!")
71
+ AssetsBooster.log("Please reorder files under 'base' so dependencies are loaded in correct order.")
72
+ end
73
+
74
+ def self.boosted_environment?
75
+ @boosted_environment ||= config['options']['environments'].include?(Rails.env)
76
+ end
77
+
78
+ private
79
+
80
+ def self.file_list(path, extension)
81
+ Dir[File.join(path, "*.#{extesnsion}")].map do |file|
82
+ file.chomp(".#{extension}")
83
+ end
84
+ end
85
+
86
+ def self.get_package(type)
87
+ require "assets_booster/package/base"
88
+ require "assets_booster/package/#{type}"
89
+ klass = "AssetsBooster::Package::#{type.to_s.camelize}"
90
+ klass.constantize
91
+ rescue LoadError => e
92
+ raise "You've specified an invalid package type '#{type}'"
93
+ end
94
+
95
+ def self.get_compiler(name)
96
+ require "assets_booster/compiler/#{name}"
97
+ klass = "AssetsBooster::Compiler::#{name.to_s.camelize}"
98
+ klass.constantize
99
+ rescue LoadError => e
100
+ raise "You've specified an invalid compiler '#{name}'"
101
+ end
102
+ end
103
+ end
104
+
@@ -0,0 +1,79 @@
1
+ module AssetsBooster
2
+ module Merger
3
+ class CSS
4
+ cattr_accessor :assets
5
+
6
+ def self.name
7
+ "CSS Merger"
8
+ end
9
+
10
+ def self.merge(sources, target)
11
+ self.assets = []
12
+ sources.each do |source|
13
+ load_source(source)
14
+ end
15
+
16
+ target_folder = File.dirname(target)
17
+ assets.inject("") do |code, asset|
18
+ source_folder = File.dirname(asset[:source])
19
+ rewrite_urls!(asset[:css], source_folder, target_folder)
20
+ code << asset[:css]+"\n"
21
+ end.strip
22
+ end
23
+
24
+ def self.mtime(sources)
25
+ self.assets = []
26
+ sources.each do |source|
27
+ load_source(source)
28
+ end
29
+ assets.map{ |asset| File.mtime(asset[:source]) }.max
30
+ end
31
+
32
+ private
33
+
34
+ def self.load_source(source)
35
+ css = File.read(source)
36
+ source_folder = File.dirname(source)
37
+ css.gsub!(/@import\s+([^;\n\s]+)/).each do |import|
38
+ url = $1.gsub(/^url\((.+)\)/i, '\1')
39
+ url, quotes = extract_url(url.strip)
40
+
41
+ # we don't want to statically import external stylesheets
42
+ next import if absolute_url?(url)
43
+
44
+ # recursively process the imported css
45
+ load_source(source_folder+"/"+url)
46
+ ""
47
+ end
48
+ self.assets << {
49
+ :source => source,
50
+ :css => css
51
+ }
52
+ end
53
+
54
+ def self.rewrite_urls!(css, source_folder, target_folder)
55
+ # difference between the source and target location
56
+ url_prepend = source_folder[target_folder.length+1..-1]
57
+ return unless url_prepend
58
+
59
+ css.gsub!(/url\(([^)]+)\)/i) do |match|
60
+ url, quotes = extract_url($1.strip)
61
+
62
+ # we don't want to change references to external assets
63
+ next match if absolute_url?(url)
64
+
65
+ "url(#{quotes}#{url_prepend}/#{url}#{quotes})"
66
+ end
67
+ end
68
+
69
+ def self.extract_url(quoted_url)
70
+ (quoted_url[0].chr =~ /["']/) ? [quoted_url.slice(1, quoted_url.length-2), quoted_url[0].chr] : [quoted_url, ""]
71
+ end
72
+
73
+ def self.absolute_url?(url)
74
+ url[0].chr =~ /^(\/|https?:\/\/)/i
75
+ end
76
+ end
77
+ end
78
+ end
79
+