source_map-jsmin 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/source_map/jsmin.rb +332 -0
  2. metadata +68 -0
@@ -0,0 +1,332 @@
1
+ #--
2
+ # jsmin.rb - Ruby implementation of Douglas Crockford's JSMin.
3
+ #
4
+ # This is a port of jsmin.c, and is distributed under the same terms, which are
5
+ # as follows:
6
+ #
7
+ # Copyright (c) 2002 Douglas Crockford (www.crockford.com)
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # of this software and associated documentation files (the "Software"), to deal
11
+ # in the Software without restriction, including without limitation the rights
12
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # copies of the Software, and to permit persons to whom the Software is
14
+ # furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in all
17
+ # copies or substantial portions of the Software.
18
+ #
19
+ # The Software shall be used for Good, not Evil.
20
+ #
21
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ # SOFTWARE.
28
+ #++
29
+
30
+ require 'strscan'
31
+
32
+ require 'rubygems'
33
+ require 'source_map'
34
+ require 'json'
35
+
36
+ # = JSMin
37
+ #
38
+ # Ruby implementation of Douglas Crockford's JavaScript minifier, JSMin.
39
+ #
40
+ # *Author*:: Ryan Grove (mailto:ryan@wonko.com)
41
+ # *Version*:: 1.0.1 (2008-11-10)
42
+ # *Copyright*:: Copyright (c) 2008 Ryan Grove. All rights reserved.
43
+ # *Website*:: http://github.com/rgrove/jsmin
44
+ #
45
+ # == Example
46
+ #
47
+ # require 'rubygems'
48
+ # require 'jsmin'
49
+ #
50
+ # File.open('example.js', 'r') {|file| puts JSMin.minify(file) }
51
+ #
52
+ module SourceMap::JSMin
53
+ CHR_APOS = "'".freeze
54
+ CHR_ASTERISK = '*'.freeze
55
+ CHR_BACKSLASH = '\\'.freeze
56
+ CHR_CR = "\r".freeze
57
+ CHR_FRONTSLASH = '/'.freeze
58
+ CHR_LF = "\n".freeze
59
+ CHR_QUOTE = '"'.freeze
60
+ CHR_SPACE = ' '.freeze
61
+
62
+ ORD_LF = ?\n
63
+ ORD_SPACE = ?\
64
+ ORD_TILDE = ?~
65
+
66
+ class ParseError < RuntimeError
67
+ attr_accessor :source, :line
68
+ def initialize(err, source, line)
69
+ @source = source,
70
+ @line = line
71
+ super "JSMin Parse Error: #{err} at line #{line} of #{source}"
72
+ end
73
+ end
74
+
75
+ class << self
76
+ def raise(err)
77
+ super ParseError.new(err, @source, @line)
78
+ end
79
+
80
+ # Reads JavaScript from _input_ (which can be a String or an IO object) and
81
+ # returns a String containing minified JS.
82
+ #
83
+ # opts can contain
84
+ # :input_filename # an input filename to include in the sourcemap (required).
85
+ # :output_filename # an output filename to include in the sourcemap.
86
+ # :source_root # the sourceRoot to include in the sourcemap.
87
+ #
88
+ # While the output_filename can be anything you like, in order for the source
89
+ # map to work correctly, the :source_root + :input_filename must map to a browser-
90
+ # accessible version of the input file.
91
+ #
92
+ # If you set an absolute url as :input_filename, then you don't need to set
93
+ # :source_root, but it's nicer if you separate those concerns.
94
+ #
95
+ # @return SourceMap
96
+ #
97
+ # Once you have a source map object you'll probably want to save it into files
98
+ # as in the example:
99
+ #
100
+ # map = JSMin::SourceMap.minify(File.read("foo.js"),
101
+ # :input_filename => 'foo.js',
102
+ # :output_filename => 'foo.min.js',
103
+ # :source_root => 'http://localhost:3000/javascripts/')
104
+ #
105
+ # # output the generated, minified, javascript to foo.js.min
106
+ # File.open('foo.min.js', 'w') { |f| f << map.generated_output }
107
+ #
108
+ # # output the generated source map to foo.
109
+ # File.open('foo.map.json', 'w') { |f| f << map.to_s }
110
+ #
111
+ def minify(input, opts={})
112
+ @js = StringScanner.new(input.is_a?(IO) ? input.read : input.to_s)
113
+ @source = input.is_a?(IO) ? input.inspect : input.to_s[0..100]
114
+ @line = 1
115
+ @column = 0
116
+ @file = opts[:input_filename] or raise ":input_filename is required"
117
+
118
+ @a = "\n"
119
+ @b = nil
120
+ @lookahead = nil
121
+ @source_map = SourceMap.new(:file => opts[:output_filename],
122
+ :source_root => opts[:source_root],
123
+ :generated_output => '')
124
+
125
+ action_get
126
+
127
+ while !@a.nil? do
128
+ case @a
129
+ when CHR_SPACE
130
+ if alphanum?(@b)
131
+ action_output
132
+ else
133
+ action_copy
134
+ end
135
+
136
+ when CHR_LF
137
+ if @b == CHR_SPACE
138
+ action_get
139
+ elsif @b =~ /[{\[\(+-]/
140
+ action_output
141
+ else
142
+ if alphanum?(@b)
143
+ action_output
144
+ else
145
+ action_copy
146
+ end
147
+ end
148
+
149
+ else
150
+ if @b == CHR_SPACE
151
+ if alphanum?(@a)
152
+ action_output
153
+ else
154
+ action_get
155
+ end
156
+ elsif @b == CHR_LF
157
+ if @a =~ /[}\]\)\\"+-]/
158
+ action_output
159
+ else
160
+ if alphanum?(@a)
161
+ action_output
162
+ else
163
+ action_get
164
+ end
165
+ end
166
+ else
167
+ action_output
168
+ end
169
+ end
170
+ end
171
+
172
+ @source_map
173
+ end
174
+
175
+ private
176
+
177
+ # output one character
178
+ def putc(c)
179
+ if position = c.instance_variable_get(:@position)
180
+ @source_map.add_generated c, :source_line => position.first,
181
+ :source_col => position.last,
182
+ :source => @file
183
+ else
184
+ @source_map.add_generated c
185
+ end
186
+ end
187
+
188
+ # Corresponds to action(1) in jsmin.c.
189
+ def action_output
190
+ putc @a
191
+ action_copy
192
+ end
193
+
194
+ # Corresponds to action(2) in jsmin.c.
195
+ def action_copy
196
+ @a = @b
197
+
198
+ if @a == CHR_APOS || @a == CHR_QUOTE
199
+ loop do
200
+ putc @a
201
+ @a = get
202
+
203
+ break if @a == @b
204
+
205
+ if @a[0] <= ORD_LF
206
+ raise "unterminated string literal: #{@a.inspect}"
207
+ end
208
+
209
+ if @a == CHR_BACKSLASH
210
+ putc @a
211
+ @a = get
212
+
213
+ if @a[0] <= ORD_LF
214
+ raise "unterminated string literal: #{@a.inspect}"
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ action_get
221
+ end
222
+
223
+ # Corresponds to action(3) in jsmin.c.
224
+ def action_get
225
+ @b = nextchar
226
+
227
+ if @b == CHR_FRONTSLASH && (@a == CHR_LF || @a =~ /[\(,=:\[!&|?{};]/)
228
+ putc @a
229
+ putc @b
230
+
231
+ loop do
232
+ @a = get
233
+
234
+ # Inside a regex [...] set, which MAY contain a '/' itself.
235
+ # Example:
236
+ # mootools Form.Validator near line 460:
237
+ # return Form.Validator.getValidator('IsEmpty').test(element) || (/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(element.get('value'));
238
+ if @a == '['
239
+ loop do
240
+ putc @a
241
+ @a = get
242
+ case @a
243
+ when ']' then break
244
+ when CHR_BACKSLASH then
245
+ putc @a
246
+ @a = get
247
+ when @a[0] <= ORD_LF
248
+ raise "JSMin parse error: unterminated regular expression " +
249
+ "literal: #{@a}"
250
+ end
251
+ end
252
+ elsif @a == CHR_FRONTSLASH
253
+ break
254
+ elsif @a == CHR_BACKSLASH
255
+ putc @a
256
+ @a = get
257
+ elsif @a[0] <= ORD_LF
258
+ raise "unterminated regular expression : #{@a.inspect}"
259
+ end
260
+
261
+ putc @a
262
+ end
263
+
264
+ @b = nextchar
265
+ end
266
+ end
267
+
268
+ # Returns true if +c+ is a letter, digit, underscore, dollar sign,
269
+ # backslash, or non-ASCII character.
270
+ def alphanum?(c)
271
+ c.is_a?(String) && !c.empty? && (c[0] > ORD_TILDE || c =~ /[0-9a-z_$\\]/i)
272
+ end
273
+
274
+ # Returns the next character from the input. If the character is a control
275
+ # character, it will be translated to a space or linefeed.
276
+ def get
277
+ if @lookahead
278
+ c = @lookahead
279
+ @lookahead = nil
280
+ else
281
+ c = @js.getch
282
+ c.instance_variable_set(:@position, [@line, @column]) if c
283
+ if c == CHR_LF || c == CHR_CR
284
+ @line += 1
285
+ @column = 0
286
+ return CHR_LF
287
+ else
288
+ @column += 1
289
+ end
290
+ return ' ' unless c.nil? || c[0] >= ORD_SPACE
291
+ end
292
+ c
293
+ end
294
+
295
+ # Gets the next character, excluding comments.
296
+ def nextchar
297
+ c = get
298
+ return c unless c == CHR_FRONTSLASH
299
+
300
+ case peek
301
+ when CHR_FRONTSLASH
302
+ loop do
303
+ c = get
304
+ return c if c[0] <= ORD_LF
305
+ end
306
+
307
+ when CHR_ASTERISK
308
+ get
309
+ loop do
310
+ case get
311
+ when CHR_ASTERISK
312
+ if peek == CHR_FRONTSLASH
313
+ get
314
+ return ' '
315
+ end
316
+
317
+ when nil
318
+ raise 'unterminated comment'
319
+ end
320
+ end
321
+
322
+ else
323
+ return c
324
+ end
325
+ end
326
+
327
+ # Gets the next character without getting it.
328
+ def peek
329
+ @lookahead = get
330
+ end
331
+ end
332
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: source_map-jsmin
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Conrad Irwin
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-02-29 00:00:00 -08:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: This is a hacked about version of Ryan Grove's original Ruby port of jsmin that addssupport for generating a source map, using the source_map gem
22
+ email: conrad.irwin@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/source_map/jsmin.rb
31
+ has_rdoc: true
32
+ homepage: http://github.com/ConradIrwin/source_map-jsmin
33
+ licenses: []
34
+
35
+ post_install_message:
36
+ rdoc_options: []
37
+
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 59
46
+ segments:
47
+ - 1
48
+ - 8
49
+ - 6
50
+ version: 1.8.6
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.7
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: A ruby port of Douglas Crockford's jsmin, with support for generating source maps
67
+ test_files: []
68
+