packr 3.1.1 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,18 @@
1
+ === 3.2.1 / 2013-02-10
2
+
3
+ * 3.2.0 has been yanked due to unexpected files in the download -- this is replacing it
4
+
5
+
6
+ === 3.2.0 / 2012-04-02
7
+
8
+ * Added source map generation for all compression modes
9
+
10
+
1
11
  === 3.1.1 / 2011-07-19
2
12
 
3
13
  * Remove dependency on Hoe
4
14
 
15
+
5
16
  === 3.1.0 / 2009-02-22
6
17
 
7
18
  * Project is now a gem, not a Rails plugin
@@ -10,10 +21,12 @@
10
21
  * Changed variable protection, protected variables must be supplied
11
22
  as options to Packr#pack.
12
23
 
24
+
13
25
  === 1.0.2 / 2007-12-13
14
26
 
15
27
  * Added variable protection, protecting $super for use with Prototype
16
28
 
29
+
17
30
  === 1.0.0 / 2007-12-04
18
31
 
19
32
  * Initial release, compatible with Packer 3.0
@@ -1,24 +1,19 @@
1
- = PackR
1
+ = Packr {<img src="https://secure.travis-ci.org/jcoglan/packr.png" alt="Build Status" />}[http://travis-ci.org/jcoglan/packr]
2
2
 
3
- * http://github.com/jcoglan/packr
4
- * http://dean.edwards.name/packer/
5
- * http://base2.googlecode.com
3
+ Packr is a Ruby implementation of Dean Edwards' JavaScript compressor,
4
+ {Packer}[http://dean.edwards.name/packer/]. It can remove comments and
5
+ whitespace, compress variable names, obfuscate <tt>_private</tt> identifiers,
6
+ and base-62 encode your programs. It can also generate {source
7
+ maps}[http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/], which
8
+ lets your browser trace log messages and errors back to the original source,
9
+ even when running minified code.
6
10
 
11
+ == Usage
7
12
 
8
- == Description
13
+ Packr provides both a command-line interface and a Ruby API. To call it from the
14
+ command line (use <tt>packr --help</tt> to see available options):
9
15
 
10
- PackR is a Ruby version of Dean Edwards' JavaScript compressor.
11
-
12
-
13
- == Features
14
-
15
- * Whitespace and comment removal
16
- * Compression of local variable names
17
- * Compression and obfuscation of 'private' (_underscored) identifiers
18
- * Base-62 encoding
19
-
20
-
21
- == Synopsis
16
+ packr my_script.js > my_script.min.js
22
17
 
23
18
  To call from within a Ruby program:
24
19
 
@@ -26,7 +21,7 @@ To call from within a Ruby program:
26
21
 
27
22
  code = File.read('my_script.js')
28
23
  compressed = Packr.pack(code)
29
- File.open('my_script.min.js', 'wb') { |f| f.write(compressed) }
24
+ File.open('my_script.min.js', 'w') { |f| f.write(compressed) }
30
25
 
31
26
  This method takes a number of options to control compression, for example:
32
27
 
@@ -34,19 +29,83 @@ This method takes a number of options to control compression, for example:
34
29
 
35
30
  The full list of available options is:
36
31
 
32
+ * <tt>:minify</tt> -- set to +false+ to prevent any compression; this is useful
33
+ if you just want to use <tt>Packr.bundle</tt> to concatenate files and
34
+ generate a source map
37
35
  * <tt>:shrink_vars</tt> -- set to +true+ to compress local variable names
38
36
  * <tt>:private</tt> -- set to +true+ to obfuscate 'private' identifiers, i.e.
39
37
  names beginning with a single underscore
40
38
  * <tt>:base62</tt> -- encode the program using base 62
41
- * <tt>:protect</tt> -- an array of variable names to protect from compression, e.g.
39
+ * <tt>:protect</tt> -- an array of variable names to protect from compression
40
+ * <tt>:header</tt> -- an optional string to prepend to the output, e.g. for
41
+ copyright comments
42
42
 
43
- compressed = Packr.pack(code, :shrink_vars => true,
44
- :protect => %w[$super self])
43
+ Here's an example using all of these:
45
44
 
46
- To call from the command line (use <tt>packr --help</tt> to see available
47
- options):
45
+ compressed = Packr.pack(code,
46
+ :shrink_vars => true,
47
+ :private => true,
48
+ :protect => %w[$super self],
49
+ :header => '/* Copyright 2012 some guy */'
50
+ )
48
51
 
49
- packr my_script.js > my_script.min.js
52
+
53
+ === Bundling and source maps
54
+
55
+ Packr also provides an API for bundling several files together and generating a
56
+ single output file and {source
57
+ map}[http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/]. For
58
+ example, given these two files:
59
+
60
+ # example_a.js
61
+
62
+ 1. // When the minified code is loaded into a browser, you should see the call to
63
+ 2. // console.log() attributed to example_a.js:4
64
+ 3.
65
+ 4. console.log('Hello from file A');
66
+
67
+ and
68
+
69
+ # example_b.js
70
+
71
+ 1. var display = function(message) {
72
+ 2. alert(message + ' from file B');
73
+ 3. };
74
+ 4.
75
+ 5. display('Ahoy there');
76
+
77
+
78
+ it can generate a combined output file:
79
+
80
+ # example-min.js
81
+
82
+ /* Copyright 2012 */
83
+ console.log('Hello from file A');var display=function(a){alert(a+' from file B')};display('Ahoy there');
84
+ //@ sourceMappingURL=example-min.js.map
85
+
86
+ and a source map:
87
+
88
+ # example-min.js.map
89
+
90
+ {
91
+ "version": 3,
92
+ "file": "example-min.js",
93
+ "sourceRoot": "",
94
+ "sources": ["../example_a.js", "../example_b.js"],
95
+ "names": ["message"],
96
+ "mappings": ";AAGA,QAAQ,KAAK,MAAM,KAAK,KAAK,ICH7B,IAAI,QAAU,SAASA,GACrB,MAAMA,IAAY,KAAK,KAAK,KAG9B,SAAS,KAAK;"
97
+ }
98
+
99
+ To do this, you use the +bundle+ method, passing an array of input files and a
100
+ single output file, and the compression options you want to use.
101
+
102
+ Packr.bundle %w[example_a.js example_b.js], 'min/example-min.js',
103
+ :shrink_vars => true,
104
+ :header => '/* Copyright 2012 */'
105
+
106
+ On the command line, this looks like:
107
+
108
+ packr example_a.js example_b.js -o min/example-min.js -h '/* Copyright 2012 */' --shrink-vars
50
109
 
51
110
 
52
111
  == Notes
@@ -56,39 +115,26 @@ expressions. Be sure to include semicolons and braces everywhere they are
56
115
  required so that your program will work correctly when packed down to a single
57
116
  line.
58
117
 
59
- By far the most efficient way to serve JavaScript over the web is to use PackR
60
- with the --shrink-vars flag, combined with gzip compression. If you don't have
61
- access to your server config to set up mod_deflate, you can generate gzip files
62
- using (on Unix-like systems):
63
-
64
- packr -s my-file.js | gzip > my-file.js.gz
65
-
66
- You can then get Apache to serve the files by putting this in your .htaccess
67
- file:
68
-
69
- AddEncoding gzip .gz
70
- RewriteCond %{HTTP:Accept-encoding} gzip
71
- RewriteCond %{HTTP_USER_AGENT} !Safari
72
- RewriteCond %{REQUEST_FILENAME}.gz -f
73
- RewriteRule ^(.*)$ $1.gz [QSA,L]
118
+ By far the most efficient way to serve JavaScript over the web is to use Packr
119
+ with the <tt>--shrink-vars</tt> flag, combined with gzip compression.
74
120
 
75
- If you really cannot serve gzip files, use the --base62 option to further
76
- compress your code. This mode is at its best when compressing large files with
77
- many repeated tokens.
121
+ If you really cannot serve gzip files, use the <tt>--base62</tt> option to
122
+ further compress your code. This mode is at its best when compressing large
123
+ files with many repeated tokens.
78
124
 
79
- The --private option can be used to stop other programs calling private methods
80
- in your code by renaming anything beginning with a single underscore. Beware
81
- that you should not use this if the generated file contains 'private' methods
82
- that need to be accessible by other files. Also know that all the files that
83
- access any particular private method must be compressed together so they all get
84
- the same rewritten name for the private method.
125
+ The <tt>--private</tt> option can be used to stop other programs calling private
126
+ methods in your code by renaming anything beginning with a single underscore.
127
+ Beware that you should not use this if the generated file contains 'private'
128
+ methods that need to be accessible by other files. Also know that all the files
129
+ that access any particular private method must be compressed together so they
130
+ all get the same rewritten name for the private method.
85
131
 
86
132
 
87
133
  == License
88
134
 
89
135
  (The MIT License)
90
136
 
91
- Copyright (c) 2004-2011 Dean Edwards, James Coglan
137
+ Copyright (c) 2004-2012 Dean Edwards, James Coglan
92
138
 
93
139
  Permission is hereby granted, free of charge, to any person obtaining a copy of
94
140
  this software and associated documentation files (the 'Software'), to deal in
data/bin/packr CHANGED
@@ -5,76 +5,78 @@ require 'oyster'
5
5
  require File.expand_path('../../lib/packr', __FILE__)
6
6
 
7
7
  spec = Oyster.spec do
8
- name "packr -- JavaScript code compressor based on Dean Edwards' Packer"
8
+ name "packr -- JavaScript code compressor based on Dean Edwards' Packer"
9
9
 
10
10
  synopsis <<-EOS
11
- packr [OPTIONS] INPUT_FILES > OUTPUT_FILE
11
+ packr [OPTIONS] INPUT_FILES -o OUTPUT_FILE
12
12
  cat INPUT_FILES | packr [OPTIONS] > OUTPUT_FILE
13
13
  EOS
14
14
 
15
15
  description <<-EOS
16
- PackR is a program for compressing JavaScript programs. It can remove
17
- whitespace and comments, compress local variable names, compress/obfuscate
18
- private identifiers, and encode the program in base-62.
16
+ Packr is a program for compressing JavaScript programs. It can remove comments
17
+ and whitespace, compress variable names, obfuscate '_private' identifiers, and
18
+ base-62 encode your programs. It can also generate source maps, which lets
19
+ your browser trace log messages and errors back to the original source, even
20
+ when running minified code.
19
21
 
20
22
  When invoked from the command line, it concatenates all the code in
21
23
  INPUT_FILES (or from standard input) and compresses the code using the given
22
- options, printing the result to standard output. You can pipe this output into
23
- another file to save it.
24
+ options, printing the result to standard output. If the --output option is
25
+ used, the result is written to the given file along with a source map.
24
26
  EOS
25
27
 
26
- flag :'shrink-vars', :default => true,
28
+ flag :minify, :default => true,
29
+ :desc => 'Remove unnecessary whitespace, comments and other tokens'
30
+
31
+ flag :'shrink-vars', :default => true,
27
32
  :desc => 'Shrink local variable names inside functions'
28
33
 
29
- flag :private, :default => false,
34
+ flag :private, :default => false,
30
35
  :desc => 'Obfuscate private identifiers, i.e. names beginning with a single underscore'
31
36
 
32
- flag :base62, :default => false,
37
+ flag :base62, :default => false,
33
38
  :desc => 'Encode the program using base 62'
34
39
 
35
- array :protect, :default => [],
40
+ array :protect, :default => [],
36
41
  :desc => 'List of variable names to protect from compression when using --shrink-vars'
37
42
 
43
+ string :header,
44
+ :desc => 'Optional string to prepend to the output, e.g. for copyright comments'
45
+
46
+ string :output,
47
+ :desc => <<-EOS
48
+ Path to which to write compressed output; output will be written to stdout
49
+ unless this is passed. If this option is used with a list of INPUT_FILES then
50
+ a source map will also be written to OUTPUT_FILE.map.
51
+ EOS
52
+
38
53
  notes <<-EOS
39
54
  This program is not a JavaScript parser, and rewrites your files using regular
40
55
  expressions. Be sure to include semicolons and braces everywhere they are
41
56
  required so that your program will work correctly when packed down to a single
42
57
  line.
43
58
 
44
- By far the most efficient way to serve JavaScript over the web is to use PackR
45
- with the --shrink-vars flag, combined with gzip compression. If you don't have
46
- access to your server config to set up mod_deflate, you can generate gzip
47
- files using (on Unix-like systems):
48
-
49
- packr -s my-file.js | gzip > my-file.js.gz
50
-
51
- You can then get Apache to serve the files by putting this in your .htaccess
52
- file:
53
-
54
- AddEncoding gzip .gz
55
- RewriteCond %{HTTP:Accept-encoding} gzip
56
- RewriteCond %{HTTP_USER_AGENT} !Safari
57
- RewriteCond %{REQUEST_FILENAME}.gz -f
58
- RewriteRule ^(.*)$ $1.gz [QSA,L]
59
+ By far the most efficient way to serve JavaScript over the web is to use Packr
60
+ with the --shrink-vars flag, combined with gzip compression.
59
61
 
60
62
  If you really cannot serve gzip files, use the --base62 option to further
61
63
  compress your code. This mode is at its best when compressing large files with
62
64
  many repeated tokens.
63
65
 
64
- The --private option can be used to stop other programs calling private
65
- methods in your code by renaming anything beginning with a single underscore.
66
- Beware that you should not use this if the generated file contains 'private'
67
- methods that need to be accessible by other files. Also know that all the
68
- files that access any particular private method must be compressed together so
69
- they all get the same rewritten name for the private method.
66
+ The --private option can be used to stop other programs calling private methods
67
+ in your code by renaming anything beginning with a single underscore. Beware
68
+ that you should not use this if the generated file contains 'private' methods
69
+ that need to be accessible by other files. Also know that all the files that
70
+ access any particular private method must be compressed together so they all
71
+ get the same rewritten name for the private method.
70
72
  EOS
71
73
 
72
74
  author <<-EOS
73
- Original JavaScript version by Dean Edwards, Ruby port by James Coglan <jcoglan@googlemail.com>
75
+ Original JavaScript version by Dean Edwards, Ruby port by James Coglan <jcoglan@gmail.com>
74
76
  EOS
75
77
 
76
78
  copyright <<-EOS
77
- Copyright (c) 2004-2011 Dean Edwards, James Coglan. This program is free
79
+ Copyright (c) 2004-2012 Dean Edwards, James Coglan. This program is free
78
80
  software, distributed under the MIT license.
79
81
  EOS
80
82
  end
@@ -82,16 +84,30 @@ end
82
84
  begin
83
85
  opts = spec.parse
84
86
 
85
- inputs = opts[:unclaimed]
86
- code = inputs.empty? ?
87
- $stdin.read :
88
- inputs.map { |f| File.read(f) }.join("\n")
89
-
90
- $stdout.puts Packr.pack(code,
91
- :shrink_vars => !!opts[:'shrink-vars'],
87
+ compression_options = {
88
+ :minify => opts[:minify],
89
+ :shrink_vars => opts[:'shrink-vars'],
90
+ :private => opts[:private],
92
91
  :protect => opts[:protect],
93
- :private => !!opts[:private],
94
- :base62 => !!opts[:base62])
92
+ :base62 => opts[:base62],
93
+ :header => opts[:header]
94
+ }
95
+
96
+ inputs = opts[:unclaimed]
97
+ output = opts[:output]
98
+
99
+ if inputs.empty?
100
+ result = Packr.pack($stdin.read, compression_options)
101
+ if output
102
+ FileUtils.mkdir_p(File.expand_path('..', output))
103
+ File.open(output, 'w') { |f| f.write(result) }
104
+ else
105
+ $stdout.puts(result)
106
+ end
107
+ else
108
+ result = Packr.bundle(inputs, output, compression_options)
109
+ $stdout.puts(result) unless output
110
+ end
95
111
 
96
112
  rescue Oyster::HelpRendered
97
113
  end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require File.expand_path('../../lib/packr', __FILE__)
3
+
4
+ dir = File.expand_path('..', __FILE__)
5
+ sources = ["#{dir}/script_a.js", "#{dir}/script_b.js"]
6
+
7
+ Packr.bundle(sources => "#{dir}/min/script-min.js",
8
+ :shrink_vars => true,
9
+ :private => true,
10
+ :header => '/* Copyright 2012 some guy */'
11
+ )
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
6
+ <title>Source maps test</title>
7
+ <script type="text/javascript" src="/min/script-min.js"></script>
8
+ </head>
9
+ <body>
10
+ <h1>Source maps test</h1>
11
+ </body>
12
+ </html>
@@ -0,0 +1,4 @@
1
+ // When the minified code is loaded into a browser, you should see the call to
2
+ // console.log() attributed to script_a.js:4
3
+
4
+ console.log('Hello from file A');
@@ -0,0 +1,32 @@
1
+ console.log('Hello from file B');
2
+
3
+ (function(config) {
4
+ /* Log a few messages... */
5
+ for (var index = 0; index < 2; index++) {
6
+ console.log('Message ' + index);
7
+ console.log('Again! ' + index);
8
+ }
9
+
10
+ var object = {
11
+ /**
12
+ * object#name -> undefined
13
+ * @throws Error
14
+ **/
15
+ _name: function() {
16
+ throw new Error('Oh noes!');
17
+ }
18
+ };
19
+
20
+ // Find out where errors come from. We call something that throws an error
21
+ // and console.log() the stack trace.
22
+ try {
23
+ object._nonesuch();
24
+ } catch (e) {
25
+ console.log(e.stack);
26
+ }
27
+
28
+ /*
29
+ Then we can something else and let the browser deal with it.
30
+ */
31
+ object._name();
32
+ })(window);
@@ -1,20 +1,29 @@
1
- [ '/string',
2
- '/packr/map',
3
- '/packr/collection',
4
- '/packr/regexp_group',
5
- '/packr/constants',
6
- '/packr/encoder',
7
- '/packr/parser',
8
- '/packr/minifier',
9
- '/packr/privates',
10
- '/packr/shrinker',
11
- '/packr/words',
12
- '/packr/base62'
13
- ].each do |path|
14
- require File.dirname(__FILE__) + path
15
- end
1
+ require 'erb'
2
+ require 'fileutils'
3
+ require 'forwardable'
4
+ require 'set'
5
+ require 'strscan'
16
6
 
17
7
  class Packr
8
+ dir = File.expand_path('../packr', __FILE__)
9
+
10
+ autoload :Collection, dir + '/base2/collection'
11
+ autoload :Map, dir + '/base2/map'
12
+ autoload :RegexpGroup, dir + '/base2/regexp_group'
13
+
14
+ autoload :Base62, dir + '/base62'
15
+ autoload :Encoder, dir + '/encoder'
16
+ autoload :FileSystem, dir + '/file_system'
17
+ autoload :Minifier, dir + '/minifier'
18
+ autoload :Parser, dir + '/parser'
19
+ autoload :Privates, dir + '/privates'
20
+ autoload :Shrinker, dir + '/shrinker'
21
+ autoload :SourceMap, dir + '/source_map'
22
+ autoload :Words, dir + '/words'
23
+
24
+ IGNORE = RegexpGroup::IGNORE
25
+ REMOVE = ""
26
+ SPACE = " "
18
27
 
19
28
  DATA = Parser.new.
20
29
  put("STRING1", IGNORE).
@@ -22,6 +31,12 @@ class Packr
22
31
  put("CONDITIONAL", IGNORE). # conditional comments
23
32
  put("(OPERATOR)\\s*(REGEXP)", "\\1\\2")
24
33
 
34
+ module StringExtension
35
+ attr_accessor :source_map, :code
36
+ extend Forwardable
37
+ def_delegators :source_map, :header, :footer
38
+ end
39
+
25
40
  def self.encode62(c)
26
41
  (c < 62 ? '' : encode62((c / 62.0).to_i)) +
27
42
  ((c = c % 62) > 35 ? (c+29).chr : c.to_s(36))
@@ -39,8 +54,11 @@ class Packr
39
54
  end
40
55
 
41
56
  def self.pack(script, options = {})
42
- @packr ||= self.new
43
- @packr.pack(script, options)
57
+ new.pack(script, options)
58
+ end
59
+
60
+ def self.bundle(*options)
61
+ FileSystem.bundle(*options)
44
62
  end
45
63
 
46
64
  def initialize
@@ -51,12 +69,21 @@ class Packr
51
69
  end
52
70
 
53
71
  def pack(script, options = {})
54
- script = @minifier.minify(script)
55
- script = @shrinker.shrink(script, options[:protect]) if options[:shrink_vars]
56
- script = @privates.encode(script) if options[:private]
57
- script = @base62.encode(script) if options[:base62]
58
- script
59
- end
60
-
72
+ minify = (options[:minify] != false)
73
+ source_map = SourceMap.new(script, options)
74
+ script = source_map.source_code
75
+
76
+ if minify
77
+ script = @minifier.minify(script) { |sections| source_map.remove(sections) }
78
+ end
79
+
80
+ script = @shrinker.shrink(script, options[:protect]) if minify && options[:shrink_vars]
81
+ script = @privates.encode(script) if minify && options[:private]
82
+
83
+ source_map.update(script)
84
+ script = @base62.encode(script) if minify && options[:base62]
85
+
86
+ source_map.append_metadata(script)
87
+ end
61
88
  end
62
89