minjs 0.1.2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8242f875c695e92b930cd716c7c8979fe8164607
4
+ data.tar.gz: 9ca6b42d5d6db0894c65fd64ab2f89cde0e16e6e
5
+ SHA512:
6
+ metadata.gz: b9948715e0ac544058ea8437b1bdaeab2a16709d9aaf3b784d42e7e0b730ba07bc35540533f6c299f0320df079cf77b6ebf5aadcb2d2680e61184bc5ab6ac5bb
7
+ data.tar.gz: 24400fa76290a15563d3c4384ed9697761f889e69313aa445faacc399b1774ea0011e2ded343727f442385c740c40928c38f657a6d2d255c23b12b4ddf8980d4
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in minjs.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Issei Numata
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Minjs
2
+
3
+ Minjs provide minifying JavaScript code.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'minjs'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install minjs
20
+
21
+ ## Usage
22
+
23
+ From command line, type:
24
+
25
+ $ minjs <your javascript file>
26
+
27
+ For rails, add next line to your `config/environments/production.rb`
28
+
29
+ config.assets.js_compressor = Minjs::MinjsCompressor
30
+
31
+ ## Development
32
+
33
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
34
+
35
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
36
+
37
+ ## Contributing
38
+
39
+ 1. Fork it ( https://github.com/i10a/minjs/fork )
40
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
41
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
42
+ 4. Push to the branch (`git push origin my-new-feature`)
43
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "minjs"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/exe/minjs ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minjs'
4
+
5
+ argv = ARGV.dup
6
+ f = []
7
+ argv.each do |x|
8
+ f.push(open(x.to_s).read())
9
+ end
10
+ prog = Minjs::Compressor.new(:debug => false)
11
+ prog.compress(f.join("\n"))
12
+ puts prog.to_js({})
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env ruby
2
+ require 'minjs/lex'
3
+ require 'minjs/ecma262'
4
+ require 'minjs/literal'
5
+ require 'minjs/statement'
6
+ require 'minjs/expression'
7
+ require 'minjs/func'
8
+ require 'minjs/program'
9
+ require 'minjs/exceptions'
10
+
11
+ module Minjs
12
+ class Compressor
13
+ include Literal
14
+ include Statement
15
+ include Exp
16
+ include Func
17
+ include Program
18
+
19
+ attr_reader :prog
20
+
21
+ def initialize(options = {})
22
+ @debug = false
23
+ if options[:debug]
24
+ @debug = true
25
+ end
26
+ end
27
+
28
+ def debug
29
+ #@global_context.debug
30
+ puts @prog.to_js()
31
+ end
32
+
33
+ def to_js(options = {})
34
+ @prog.to_js(options)
35
+ end
36
+
37
+ def compress(data, options = {})
38
+ parse(data)
39
+
40
+ reorder_function_decl
41
+ return_after
42
+ simple_replacement
43
+ reorder_var
44
+ assignment_after_var
45
+ grouping_statement
46
+ block_to_exp
47
+ if_to_cond #buggy
48
+ compress_var
49
+ reduce_exp
50
+ @heading_comments.reverse.each do |c|
51
+ @prog.source_elements.unshift(c)
52
+ end
53
+
54
+ to_js(options)
55
+ end
56
+
57
+ def parse(data)
58
+ @lex = Minjs::Lex.new(data)
59
+ @global_context = ECMA262::Context.new
60
+
61
+ @heading_comments = []
62
+ @lex.eval_lit{
63
+ while a = @lex.ws_lit
64
+ @heading_comments.push(a)
65
+ end
66
+ nil
67
+ }
68
+ @prog = source_elements(@lex, @global_context)
69
+ @prog
70
+ end
71
+
72
+ def traverse(&block)
73
+ @prog.traverse(nil, &block)
74
+ end
75
+
76
+ def next_sym(s)
77
+ def c2i(c)
78
+ c = c.ord
79
+ if c >= 0x30 and c <= 0x39
80
+ c = c - 0x30
81
+ elsif c >= 0x61 and c <= 0x7a
82
+ c = c - 0x61 + 10
83
+ elsif c >= 0x41 and c <= 0x5a
84
+ c = c - 0x41 + 10 + 26
85
+ elsif c == 0x5f
86
+ c = 62
87
+ elsif c == 0x24
88
+ c = 63
89
+ end
90
+ end
91
+ def i2c(c)
92
+ if c < 10
93
+ c = "%c" % (0x30 + c)
94
+ elsif c < 10 + 26
95
+ c = "%c" % (0x61 + c - 10)
96
+ elsif c < 10 + 26 + 26
97
+ c = "%c" % (0x41 + c - 10 - 26)
98
+ elsif c < 63
99
+ c = "_"
100
+ elsif c < 64
101
+ c = "$"
102
+ end
103
+ end
104
+
105
+ v = 0
106
+ s.to_s.split("").each do |x|
107
+ v *= 64
108
+ v += c2i(x)
109
+ end
110
+
111
+ while true
112
+ v += 1
113
+ ret = []
114
+ vv = v
115
+ while vv > 0
116
+ ret.unshift(i2c(vv % 64))
117
+ vv /= 64
118
+ end
119
+ ret = ret.join("")
120
+ if ECMA262::IdentifierName.reserved?(ret.to_sym)
121
+ ;
122
+ elsif ret.to_s.match(/^\d/)
123
+ ;
124
+ else
125
+ break
126
+ end
127
+ end
128
+ ret.to_sym
129
+
130
+ end
131
+
132
+ def grouping_statement
133
+ self.traverse {|st, parent|
134
+ if st.kind_of? ECMA262::Prog
135
+ st.grouping
136
+ elsif st.kind_of? ECMA262::StList
137
+ st.grouping
138
+ end
139
+ }
140
+ end
141
+
142
+ def reorder_function_decl
143
+ self.traverse {|st, parent|
144
+ if st.kind_of? ECMA262::StFunc and parent.kind_of? ECMA262::Prog and st.decl
145
+ if parent.index(st)
146
+ parent.remove(st)
147
+ parent.source_elements.unshift(st)
148
+ end
149
+ end
150
+ }
151
+ end
152
+
153
+ def reorder_var
154
+ #traverse all statemtns and expression
155
+ self.traverse {|st, parent|
156
+ if st.kind_of? ECMA262::Prog
157
+ vars = nil
158
+ context = nil
159
+ st.instance_eval{
160
+ vars = @context.var_env.record.binding.find_all {|k, v|
161
+ v and v[:_parameter_list].nil? and !v[:value].kind_of?(ECMA262::StFunc)
162
+ }.collect{|x|
163
+ [
164
+ ECMA262::IdentifierName.new(@context, x[0])
165
+ ]
166
+ }
167
+ st.traverse(parent) {|st2, parent2|
168
+ if st2.kind_of? ECMA262::StVar and st2.context == @context
169
+ st2.instance_eval{
170
+ blk = []
171
+ @vars.each do |vl|
172
+ if vl[1]
173
+ blk.push(ECMA262::StExp.new(ECMA262::ExpAssign.new(vl[0], vl[1])))
174
+ #parent2.replace(st2, ECMA262::StExp.new(ECMA262::ExpAssign.new(vl[0], vl[1])))
175
+ else
176
+ #parent2.replace(st2, ECMA262::StEmpty.new())
177
+ end
178
+ end
179
+ parent2.replace(st2, ECMA262::StBlock.new(ECMA262::StList.new(blk)))
180
+ }
181
+ elsif st2.kind_of? ECMA262::StForVar and st2.context == @context
182
+ parent2.replace(st2, st2.to_st_for)
183
+ elsif st2.kind_of? ECMA262::StForInVar and st2.context == @context
184
+ parent2.replace(st2, st2.to_st_for_in)
185
+ end
186
+ }
187
+ if vars.length > 0
188
+ @source_elements.unshift ECMA262::StVar.new(@context, vars)
189
+ end
190
+ }
191
+ end
192
+ }
193
+ remove_block_in_block
194
+ end
195
+
196
+ def remove_block_in_block
197
+ while true
198
+ _retry = false
199
+ self.traverse {|st, parent|
200
+ if parent.kind_of? ECMA262::Prog and st.kind_of? ECMA262::StBlock
201
+ idx = parent.index(st)
202
+ parent.source_elements[idx..idx] = st.statement_list.statement_list
203
+ _retry = true
204
+ break
205
+ elsif parent.kind_of? ECMA262::StList and st.kind_of? ECMA262::StBlock
206
+ idx = parent.index(st)
207
+ parent.statement_list[idx..idx] = st.statement_list.statement_list
208
+ _retry = true
209
+ break
210
+ elsif st.kind_of? ECMA262::StBlock
211
+ ;
212
+ end
213
+ }
214
+ break if !_retry
215
+ end
216
+ end
217
+
218
+ def block_to_exp
219
+ self.traverse {|st, parent|
220
+ if st.kind_of? ECMA262::StBlock and st.to_exp?
221
+ if parent.kind_of? ECMA262::StTry
222
+ else
223
+ t = st.to_exp({})
224
+ parent.replace(st, ECMA262::StExp.new(t))
225
+ end
226
+ end
227
+ }
228
+ end
229
+
230
+ def if_to_cond
231
+ #traverse all statemtns and expression
232
+ self.traverse {|st, parent|
233
+ if st.kind_of? ECMA262::StIf and st.to_exp?
234
+ if t = ECMA262::StExp.new(st.to_exp({}))
235
+ parent.replace(st, t)
236
+ end
237
+ end
238
+ }
239
+ end
240
+
241
+ def compress_var
242
+ #traverse all statemtns and expression
243
+ self.traverse {|st, parent|
244
+ if st.kind_of? ECMA262::StFunc and st.context.var_env.outer
245
+ var_sym = :a
246
+ #
247
+ # collect all variables under this function
248
+ #
249
+ vars = {}
250
+ st.traverse(parent) {|st2|
251
+ if st2.kind_of? ECMA262::IdentifierName
252
+ vars[st2.val.to_sym] = true
253
+ end
254
+ }
255
+ #
256
+ # collect all var variables under this function
257
+ #
258
+ var_vars = {}
259
+ st.context.var_env.record.binding.each do|k, v|
260
+ var_vars[k] = true
261
+ end
262
+ st.traverse(parent) {|st2|
263
+ if st2.kind_of? ECMA262::StFunc
264
+ st2.context.var_env.record.binding.each do|k, v|
265
+ var_vars[k] = true
266
+ end
267
+ end
268
+ }
269
+ #
270
+ # check `eval' function is exist under this function
271
+ #
272
+ unless var_vars[:eval]
273
+ eval_flag = false
274
+ st.traverse(parent) {|st2|
275
+ if st2.kind_of? ECMA262::ExpCall and st2.name.to_js({}) == "eval"
276
+ eval_flag = true
277
+ break
278
+ end
279
+ }
280
+ if eval_flag
281
+ next
282
+ end
283
+ end
284
+ #
285
+ # check var_vars
286
+ #
287
+ var_vars.each {|name, v|
288
+ if name.nil?
289
+ next
290
+ end
291
+ while(vars[var_sym])
292
+ var_sym = next_sym(var_sym)
293
+ end
294
+ if name.to_s.bytesize > var_sym.to_s.bytesize
295
+ #
296
+ # rename `name' to `var_sym'
297
+ #
298
+ st.traverse(parent){|st2|
299
+ if st2.kind_of? ECMA262::IdentifierName and st2.context and st2.context.var_env == st.context.var_env.outer
300
+ ;
301
+ elsif st2.kind_of? ECMA262::IdentifierName and st2.val == name
302
+ st2.val = var_sym
303
+ elsif st2.kind_of? ECMA262::StFunc
304
+ st2.context.var_env.record.binding[var_sym] = st2.context.var_env.record.binding[name]
305
+ st2.context.var_env.record.binding.delete(name)
306
+ end
307
+ }
308
+ end
309
+ var_sym = next_sym(var_sym)
310
+ }
311
+ end
312
+ }
313
+ end
314
+ def reduce_exp
315
+ self.traverse {|st, parent|
316
+ if st.kind_of? ECMA262::Exp
317
+ st.reduce(parent)
318
+ end
319
+ }
320
+ end
321
+
322
+ def simple_replacement
323
+ self.traverse {|st, parent|
324
+ #true => !0
325
+ #false => !1
326
+ if st.kind_of? ECMA262::Boolean
327
+ if st.true?
328
+ parent.replace(st, ECMA262::ExpLogicalNot.new(ECMA262::ECMA262Numeric.new('0', 0)))
329
+ else
330
+ parent.replace(st, ECMA262::ExpLogicalNot.new(ECMA262::ECMA262Numeric.new('1', 1)))
331
+ end
332
+ #if(true){<then>}else{<else>} => then
333
+ elsif st.kind_of? ECMA262::StIf
334
+ if st.cond.kind_of? ECMA262::Boolean
335
+ if st.cond.true?
336
+ parent.replace(st, st.then_st)
337
+ elsif st.else_st
338
+ parent.replace(st, st.else_st)
339
+ else
340
+ parent.replace(st, ECMA262::StEmpty.new())
341
+ end
342
+ end
343
+ end
344
+ }
345
+ end
346
+
347
+ def return_after
348
+ self.traverse {|st, parent|
349
+ if st.kind_of? ECMA262::StReturn
350
+ if parent.kind_of? ECMA262::StList
351
+ idx = parent.index(st)
352
+ idx += 1
353
+ while parent.statement_list[idx]
354
+ parent.statement_list[idx] = ECMA262::StEmpty.new;
355
+ idx += 1
356
+ end
357
+ elsif parent.kind_of? ECMA262::Prog
358
+ idx = parent.index(st)
359
+ idx += 1
360
+ while parent.source_elements[idx]
361
+ parent.source_elements[idx] = ECMA262::StEmpty.new;
362
+ idx += 1
363
+ end
364
+ if st.exp.nil?
365
+ parent.replace(st, ECMA262::StEmpty.new)
366
+ end
367
+ end
368
+ end
369
+ }
370
+ end
371
+ #
372
+ # var a; a=1
373
+ # => var a=1
374
+ #
375
+ def assignment_after_var
376
+ self.traverse {|st, parent|
377
+ if st.kind_of? ECMA262::StExp and parent.kind_of? ECMA262::Prog
378
+ if st.exp.kind_of? ECMA262::ExpAssign
379
+ idx = parent.index(st)
380
+ while idx > 0
381
+ idx -= 1
382
+ prevst = parent[idx]
383
+ if prevst.kind_of? ECMA262::StEmpty
384
+ next
385
+ elsif prevst.kind_of? ECMA262::StVar
386
+ i = 0
387
+ prevst.normalization
388
+ prevst.vars.each do |name, init|
389
+ if st.exp.val == name and init.nil?
390
+ prevst.vars[i] = [name, st.exp.val2]
391
+ parent.replace(st, ECMA262::StEmpty.new())
392
+ break
393
+ end
394
+ i += 1
395
+ end
396
+ prevst.normalization
397
+ break
398
+ else
399
+ break
400
+ end
401
+ end
402
+ end
403
+ end
404
+ }
405
+ end
406
+ end
407
+ end
408
+
409
+ if $0 == __FILE__
410
+ argv = ARGV.dup
411
+ f = []
412
+ argv.each do |x|
413
+ f.push(open(x.to_s).read())
414
+ end
415
+ prog = Minjs::Compressor.new(:debug => false)
416
+ prog.compress(f.join("\n"))
417
+ puts prog.to_js({})
418
+ end